"""
Semi-Auto Engine for Cognix
Handles semi-automatic code generation and file modifications
"""

import os
import re
import sys
import ast
import json
import asyncio
import tempfile
import subprocess
from cognix.logger import logger, err_console
from pathlib import Path
from typing import List, Dict, Optional, Any, Tuple, NamedTuple
from dataclasses import dataclass
import logging
from datetime import datetime
from cognix.ui import Icon, StatusIndicator, print_success_banner, print_error_banner, print_warning_banner
from cognix.diff_viewer import DiffViewer
from cognix.progress_zen import StepHUD
from cognix.utils import Spinner, ProgressBar

# ⭐ File Requirement Detection Pipeline
from cognix.file_detection import FileRequirementDetector

# Local imports
try:
    from .stylecode_extractor import StyleCodeExtractor
    # Note: PromptTemplateManager import removed - prompts are handled directly in _get_code_generation_system_prompt()
    from .linter_integration import LinterIntegration
except ImportError:
    from stylecode_extractor import StyleCodeExtractor
    # Note: PromptTemplateManager import removed - prompts are handled directly in _get_code_generation_system_prompt()
    from linter_integration import LinterIntegration

# Phase 1: Linter Integration
try:
    from cognix.linter_integration import LinterIntegration
except ImportError:
    LinterIntegration = None
    logger.debug("⚠ LinterIntegration not available (optional feature)")

# ============================================
# Diff Sanitizer Configuration
# ============================================
# LLMへのプロンプトで「≤5行」と指示しているため、
# Sanitizerのmarginも±5行に統一する。
# 変更時は、この定数とプロンプト内の記述を同時に更新すること。
DIFF_SANITIZER_MARGIN = 5

# ============================================
# Project Structure (LLM-based detection result)
# ============================================

@dataclass
class ProjectStructure:
    """
    LLM判定によるプロジェクト構造
    
    Attributes:
        stage: "single" (フロントエンドのみ) or "multi" (バックエンド必要)
        file_types: 必要なファイルタイプのリスト (例: ["html", "css", "js"])
        reason: 判定理由
    """
    stage: str           # "single" | "multi"
    file_types: List[str]  # ["html", "css", "js"] など
    reason: str          # 判定理由


# ============================================
# Differential Scan: Issue Cache & Modified Range
# ============================================

@dataclass
class CachedIssue:
    """
    キャッシュされたIssue情報
    
    差分スキャン機能で使用。変更されていない行のIssueを
    再検出せずにキャッシュから取得するためのデータ構造。
    
    Attributes:
        file: ファイルパス
        line: 行番号（Noneの場合はファイル全体に関するIssue）
        line_end: 終了行（範囲指定時、Noneの場合は単一行）
        issue_type: Issue種別（1-25）
        description: Issue説明
        fix: 修正提案
        content_hash: 該当行のハッシュ（変更検知用）
    """
    file: str
    line: Optional[int]
    line_end: Optional[int]
    issue_type: int
    description: str
    fix: str
    content_hash: str


@dataclass
class ModifiedRange:
    """
    変更された行範囲
    
    unified diffから抽出された変更範囲。
    差分スキャンで再検出対象を特定するために使用。
    
    Attributes:
        file: ファイルパス
        start_line: 変更開始行（1-indexed）
        end_line: 変更終了行（1-indexed、inclusive）
        diff_content: 該当部分の差分内容
    """
    file: str
    start_line: int
    end_line: int
    diff_content: str


@dataclass
class SICError:
    """
    SIC検証エラーの構造化情報
    
    3者比較プロンプトで使用。エラーの種類と詳細を
    LLMに明確に伝えるための構造化データ。
    
    Attributes:
        error_type: エラータイプ
            - "TOP_LEVEL_POLLUTION": 属性がクラス外に漏出
            - "SYMBOL_DELETION": クラス/関数の削除
            - "ATTRIBUTE_DRIFT": 重要属性の消失
            - "CLASS_DELETION": クラスの消失
            - "TYPE_ANNOTATION_DRIFT": 型ヒントの消失
            - "SYNTAX_ERROR": 構文エラー
        affected_class: 影響を受けたクラス名
        affected_items: 影響を受けた属性/シンボル/関数のリスト
        human_readable: 人間可読なエラーメッセージ（従来形式）
    """
    error_type: str
    affected_class: Optional[str]
    affected_items: List[str]
    human_readable: str


# ============================================
# Complexity Assessment
# ============================================

class ComplexityAssessor:
    """複雑度判定クラス(3層防御)"""
    
    def __init__(self):
        self.simple_keywords = self._init_simple_keywords()
        self.medium_keywords = self._init_medium_keywords()
        self.complex_keywords = self._init_complex_keywords()
        self.exclusion_patterns = self._init_exclusion_patterns()
        
        # ✅ 既存ファイルを読み込む
        self.existing_files = {}
        
        # サポートされるファイル拡張子の定義
        # repository_analyzer.pyのSUPPORTED_LANGUAGESに対応 + 追加の一般的な言語
        supported_extensions = {
            # Python
            '.py', '.pyi',
            # JavaScript/TypeScript
            '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
            # HTML/CSS
            '.html', '.htm', '.css', '.scss', '.sass', '.less',
            # 設定・データファイル
            '.json', '.yml', '.yaml', '.toml', '.ini', '.cfg', '.xml',
            # ドキュメント
            '.md', '.mdx', '.txt', '.rst',
            # その他の言語
            '.rb',      # Ruby
            '.php',     # PHP
            '.java',    # Java
            '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp',  # C/C++
            '.cs',      # C#
            '.go',      # Go
            '.rs',      # Rust
            '.swift',   # Swift
            '.kt',      # Kotlin
            '.scala',   # Scala
            '.sh', '.bash',  # Shell
            '.ps1',     # PowerShell
            '.sql',     # SQL
            '.vue',     # Vue
            '.svelte',  # Svelte
        }
        
        try:
            scanned_count = 0
            loaded_count = 0
            
            # カレントディレクトリ内のすべてのファイルをスキャン
            for file_path in Path.cwd().glob("*.*"):
                scanned_count += 1
                
                # サポートされる拡張子のみ読み込む
                if file_path.suffix.lower() in supported_extensions:
                    try:
                        content = file_path.read_text(encoding='utf-8')
                        self.existing_files[file_path.name] = content
                        loaded_count += 1
                        logger.debug(f"[ComplexityAssessor] Loaded: {file_path.name} ({len(content)} chars)")
                    except Exception as read_error:
                        logger.debug(f"[ComplexityAssessor] Failed to read {file_path.name}: {read_error}")
                        pass
            
            logger.debug(f"[ComplexityAssessor] Scanned {scanned_count} files, loaded {loaded_count} files")
            logger.debug(f"[ComplexityAssessor] Loaded files: {list(self.existing_files.keys())}")
            
        except Exception as e:
            logger.debug(f"⚠ Warning: Failed to scan some files: {e}")
            logger.debug(f"[ComplexityAssessor] Scan error: {e}")
        

        
    def _init_simple_keywords(self) -> List[str]:
        """Simple indicators (40+ keywords)"""
        return [
            # 基本的な挨拶・表示
            'hello world', 'hello', 'hi', 'greet', 'welcome',
            'display', 'print', 'show', 'output',
            
            # 基本的な計算・変換
            'simple calculator', 'basic calculator', 'add subtract',
            'calculator', 'math', 'arithmetic',
            'converter', 'conversion', 'convert',
            'temperature', 'currency', 'unit',
            
            # 基本的な生成
            'random number', 'random', 'generate number',
            'dice', 'coin flip', 'lottery',
            
            # 基本的なタイマー・時計
            'timer', 'stopwatch', 'countdown', 'counter',
            'clock', 'time display', 'current time',
            
            # 基本的なゲーム・パズル
            'fizzbuzz', 'guess number', 'guessing game',
            'tic tac toe', 'rock paper scissors',
            
            # 基本的な文字列操作
            'reverse string', 'palindrome', 'word count',
            'character count', 'string manipulation',
            
            # 修飾語
            'simple', 'basic', 'minimal', 'quick', 'easy',
            'beginner', 'starter', 'demo', 'example',
            'prototype', 'proof of concept', 'poc'
        ]
    
    def _init_medium_keywords(self) -> List[str]:
        """Medium indicators (30+ keywords)"""
        return [
            # 標準的なアプリケーション
            'todo app', 'todo list', 'task manager',
            'note taking', 'notepad', 'markdown editor',
            'file manager', 'file browser', 'file explorer',
            'image gallery', 'photo gallery',
            'contact manager', 'address book',
            'bookmark manager', 'url shortener',
            
            # 標準的な機能
            'crud', 'form validation', 'user input',
            'file upload', 'file download',
            'search function', 'filter', 'sort',
            'pagination', 'infinite scroll',
            
            # フレームワーク使用(Medium相当)
            'react app', 'vue app', 'angular app',
            'express server', 'flask app', 'django app',
            'fastapi', 'spring boot',
            
            # データベース単体使用
            'sqlite', 'local database', 'json storage',
            'csv file', 'data persistence'
        ]
    
    def _init_complex_keywords(self) -> List[str]:
        """Complex indicators (80+ keywords)"""
        return [
            # アーキテクチャパターン
            'microservice', 'monolith', 'service mesh',
            'event driven', 'event sourcing', 'cqrs',
            'hexagonal architecture', 'clean architecture',
            'domain driven design', 'ddd',
            
            # API設計
            'api gateway', 'rest api', 'restful api',
            'graphql', 'grpc', 'websocket server',
            'api versioning', 'api documentation',
            
            # 認証・認可
            'authentication system', 'authorization',
            'oauth', 'oauth2', 'openid', 'saml',
            'jwt', 'session management', 'sso',
            'multi factor', 'mfa', '2fa',
            'rbac', 'role based', 'permission system',
            
            # データベース・永続化
            'database system', 'database design',
            'database migration', 'schema design',
            'orm design', 'data modeling',
            'transaction management', 'acid',
            'eventual consistency', 'consistency model',
            
            # 分散システム
            'distributed system', 'distributed',
            'message queue', 'message broker',
            'pub sub', 'event bus',
            'service discovery', 'load balancing',
            'circuit breaker', 'retry logic',
            'saga pattern', 'distributed transaction',
            
            # スケーラビリティ
            'scalable', 'horizontal scaling', 'vertical scaling',
            'auto scaling', 'load balancer',
            'caching strategy', 'cache layer',
            'cdn', 'content delivery',
            
            # インフラ・デプロイ
            'kubernetes', 'k8s', 'docker swarm',
            'container orchestration',
            'ci cd', 'deployment pipeline',
            'blue green', 'canary deployment',
            'terraform', 'infrastructure as code',
            'ansible', 'chef', 'puppet',
            
            # 監視・運用
            'monitoring system', 'observability',
            'distributed tracing', 'log aggregation',
            'metrics collection', 'alerting system',
            'health check', 'readiness probe',
            
            # セキュリティ
            'security framework', 'encryption system',
            'key management', 'secrets management',
            'api security', 'penetration test',
            'vulnerability scan', 'security audit',
            
            # パフォーマンス
            'performance optimization', 'query optimization',
            'connection pooling', 'thread pool',
            'async processing', 'batch processing',
            
            # エンタープライズ
            'enterprise', 'production grade',
            'high availability', 'disaster recovery',
            'backup strategy', 'failover',
            'multi tenant', 'multi region',
            
            # データ処理
            'data pipeline', 'etl', 'data warehouse',
            'stream processing', 'real time processing',
            'big data', 'data lake',
            
            # 具体的な技術スタック(Complex確定)
            'elasticsearch', 'kafka', 'rabbitmq',
            'redis cluster', 'mongodb cluster',
            'postgres replication', 'mysql cluster'
        ]
    
    def _init_exclusion_patterns(self) -> Dict[str, List[str]]:
        """除外パターン定義"""
        return {
            'architecture': ['simple', 'basic', 'folder', 'file', 'directory'],
            'production': ['simple', 'basic'],
            'system': ['simple', 'basic'],
            'distributed': ['simple', 'basic'],
            'microservice': ['simple', 'basic', 'demo'],
            'enterprise': ['simple', 'basic', 'demo', 'example']
        }
    
    def assess_complexity(self, goal: str) -> Tuple[str, Dict[str, Any]]:
        """
        複雑度を3層防御で判定
        
        Args:
            goal: 実装目標の説明
            
        Returns:
            (complexity, details) のタプル
            - complexity: "simple" | "medium" | "complex"
            - details: 判定詳細情報(デバッグ用)
        """
        goal_lower = goal.lower()
        
        # Layer 1: キーワード検出
        simple_matches = [kw for kw in self.simple_keywords if kw in goal_lower]
        medium_matches = [kw for kw in self.medium_keywords if kw in goal_lower]
        complex_matches = [kw for kw in self.complex_keywords if kw in goal_lower]
        
        # Layer 2: 除外パターン適用
        complex_matches_filtered = self._apply_exclusions(complex_matches, goal_lower)
        
        # Layer 3: スコアリング
        score = self._calculate_score(
            simple_matches, 
            medium_matches, 
            complex_matches_filtered,
            goal
        )
        
        # 最終判定
        if score >= 2.0:
            complexity = "complex"
        elif score <= -0.5:
            complexity = "simple"
        else:
            complexity = "medium"
        
        # 判定詳細(デバッグ用)
        details = {
            'score': score,
            'simple_matches': simple_matches,
            'medium_matches': medium_matches,
            'complex_matches_original': complex_matches,
            'complex_matches_filtered': complex_matches_filtered,
            'word_count': len(goal.split()),
            'goal_length': len(goal)
        }
        
        return complexity, details
    
    def _apply_exclusions(self, matches: List[str], goal: str) -> List[str]:
        """除外パターンを適用してfalse positiveを削減"""
        filtered = []
        
        for match in matches:
            should_exclude = False
            
            if match in self.exclusion_patterns:
                for excl_word in self.exclusion_patterns[match]:
                    if excl_word in goal:
                        should_exclude = True
                        break
            
            if not should_exclude:
                filtered.append(match)
        
        return filtered
    
    def _calculate_score(
        self, 
        simple_matches: List[str],
        medium_matches: List[str],
        complex_matches: List[str],
        goal: str
    ) -> float:
        """スコアリング方式で複雑度を数値化"""
        score = 0.0
        
        # キーワードマッチによるスコア
        score += len(complex_matches) * 2.0
        score += len(medium_matches) * 0.5
        score -= len(simple_matches) * 1.0
        
        # ワード数によるヒューリスティック
        words = goal.split()
        word_count = len(words)
        
        if word_count <= 3:
            score -= 0.5
        elif word_count >= 20:
            score += 1.0
        elif word_count >= 10:
            score += 0.3
        
        # 技術用語密度チェック
        technical_terms = [
            'api', 'database', 'server', 'client',
            'frontend', 'backend', 'fullstack',
            'framework', 'library', 'module',
            'interface', 'integration', 'deployment',
            'service', 'component', 'middleware'
        ]
        
        goal_lower = goal.lower()
        tech_count = sum(1 for term in technical_terms if term in goal_lower)
        
        if tech_count >= 4:
            score += 1.5
        elif tech_count >= 2:
            score += 0.5
        
        # ⭐ フルスタック検出: バックエンド+フロントエンドの組み合わせ
        # この組み合わせは本質的に複雑であり、complexとして扱うべき
        backend_keywords = [
            # Pythonフレームワーク
            'flask', 'django', 'fastapi', 'bottle', 'pyramid',
            'tornado', 'sanic', 'starlette',
            # Node.jsフレームワーク
            'express', 'nestjs', 'koa', 'hapi', 'fastify',
            # 一般バックエンド
            'backend', 'server side', 'server-side',
            'rest api', 'graphql', 'websocket',
        ]
        
        frontend_keywords = [
            # 明示的フロントエンド
            'frontend', 'front-end', 'front end',
            'html', 'css', 'canvas',
            # フレームワーク
            'react', 'vue', 'angular', 'svelte',
            # UI関連
            'ui', 'user interface', 'web interface',
            'browser', 'client side', 'client-side',
        ]
        
        has_backend = any(kw in goal_lower for kw in backend_keywords)
        has_frontend = any(kw in goal_lower for kw in frontend_keywords)
        is_fullstack = has_backend and has_frontend
        
        if is_fullstack:
            # フルスタックは+2.0でcomplex確定（閾値2.0）
            score += 2.0
        
        return score


class SemiAutoResult(NamedTuple):
    """Semi-auto implementation result
    
    Attributes:
        success: 実装が成功したかどうか
        analysis: 実装目標の分析結果
        generated_code: 生成されたコード {filename: code}
        quality_scores: 品質スコア {filename: score}
        recommendations: 推奨事項のリスト
        error: エラーメッセージ(失敗時)
        impact_analysis: 影響分析結果 {filename: impact_data} (⭐ フェーズ1で追加)
        lint_result: Lint実行結果 {has_errors, errors, warnings, ...} (⭐ [t] Try again対応)
        zen_summary: Zen HUD用サマリー {lint: {...}, review: {...}} (⭐ Zen HUD対応)
    """
    success: bool
    analysis: str = ""
    generated_code: Dict[str, str] = {}
    quality_scores: Dict[str, float] = {}
    recommendations: List[str] = []
    error: str = ""
    impact_analysis: Dict[str, Any] = {}  # ⭐ 新規追加フィールド
    lint_result: Optional[Dict[str, Any]] = None  # ⭐ [t] Try again対応
    zen_summary: Optional[Dict[str, Any]] = None  # ⭐ Zen HUD用サマリー


class ApplyResult(NamedTuple):
    """Code application result"""
    success: bool
    applied_files: List[str] = []
    backup_paths: List[str] = []
    quality_scores: Dict[str, float] = {}
    error: str = ""


# ============================================
# File Details Summary for [v] View details
# ============================================

def get_file_details_summary(
    generated_code: Dict[str, str],
    quality_scores: Dict[str, float],
    lint_result: Optional[Dict[str, Any]],
    zen_summary: Optional[Dict[str, Any]],
    filename: str
) -> Dict[str, Any]:
    """
    ファイルの詳細サマリーを取得（[v] View details用）
    
    Args:
        generated_code: 生成されたコード {filename: code}
        quality_scores: 品質スコア {filename: score}
        lint_result: Lint実行結果
        zen_summary: Zen HUD用サマリー
        filename: 対象ファイル名
        
    Returns:
        ファイル詳細サマリー dict
    """
    # 行数取得
    code = generated_code.get(filename, "")
    lines = len(code.split('\n')) if code else 0
    
    # 品質スコアとグレード
    score = quality_scores.get(filename, 0.0)
    if score >= 0.9:
        grade = "Excellent"
    elif score >= 0.75:
        grade = "Good"
    elif score >= 0.6:
        grade = "Fair"
    else:
        grade = "Needs Review"
    
    # Review Issues（このファイルのみ）
    review_issues = []
    if zen_summary:
        all_issues = zen_summary.get("review", {}).get("issues", [])
        for issue in all_issues:
            issue_file = issue.get("file", "")
            # ファイル名マッチング（Windows/Unix両対応）
            # バックスラッシュとスラッシュの両方をセパレータとして扱う
            issue_basename = issue_file.replace("\\", "/").split("/")[-1]
            target_basename = filename.replace("\\", "/").split("/")[-1]
            if issue_basename == target_basename or issue_file == filename:
                review_issues.append({
                    "line": issue.get("line", "?"),
                    "type": issue.get("type", 0),  # Issue Type番号
                    "description": issue.get("description", ""),
                    "fix": issue.get("fix", "")
                })
    
    # Lint Errors/Warnings（このファイルのみ）
    lint_errors = []
    lint_warnings = []
    if lint_result:
        for err in lint_result.get("errors", []):
            err_file = err.get("file", "")
            err_basename = err_file.replace("\\", "/").split("/")[-1]
            target_basename = filename.replace("\\", "/").split("/")[-1]
            if err_basename == target_basename or err_file == filename:
                lint_errors.append({
                    "line": err.get("line", "?"),
                    "message": err.get("message", ""),
                    "rule": err.get("rule", "")
                })
        
        for warn in lint_result.get("warnings", []):
            warn_file = warn.get("file", "")
            warn_basename = warn_file.replace("\\", "/").split("/")[-1]
            target_basename = filename.replace("\\", "/").split("/")[-1]
            if warn_basename == target_basename or warn_file == filename:
                lint_warnings.append({
                    "line": warn.get("line", "?"),
                    "message": warn.get("message", ""),
                    "rule": warn.get("rule", "")
                })
    
    return {
        "filename": filename,
        "lines": lines,
        "quality_score": score,
        "quality_percent": int(score * 100),
        "quality_grade": grade,
        "review_issues": review_issues,
        "lint_errors": lint_errors,
        "lint_warnings": lint_warnings
    }


def format_file_details_for_display(details: Dict[str, Any]) -> str:
    """
    ファイル詳細を表示用文字列にフォーマット（[v] View details用）
    
    ソースコード表示後に追加表示するサマリー部分を生成
    
    Args:
        details: get_file_details_summaryの戻り値
        
    Returns:
        表示用フォーマット済み文字列
    """
    output_lines = []
    separator = "─" * 67
    
    output_lines.append("")
    output_lines.append(separator)
    output_lines.append(f"Quality: {details['quality_percent']}% {details['quality_grade']}")
    output_lines.append("")
    
    # Review Issues
    # CRITICAL Types: そもそも起動しないバグ
    # - Type 18: Circular imports → ImportError発生、プロセス即終了
    # ※ SyntaxError/IndentationErrorはLinterで検出
    # ※ 括弧アンバランス（HTML/JS）はSICで検出
    CRITICAL_TYPES = {18}  # Circular imports のみ
    
    review_issues = details.get("review_issues", [])
    if review_issues:
        output_lines.append(f"ⓘ Review Issues ({len(review_issues)}):")
        
        # 行番号あり/なしで分離してソート
        # 行番号あり → 先に表示（具体的で直しやすい）
        # 行番号なし → 後に表示（[MISSING]タグ付き）
        def has_line_number(issue):
            line = issue.get("line")
            if line is None or line == "?" or line == "None":
                return False
            # 数値または数字文字列かつ0より大きい
            try:
                return int(line) > 0
            except (ValueError, TypeError):
                return False
        
        issues_with_line = [i for i in review_issues if has_line_number(i)]
        issues_without_line = [i for i in review_issues if not has_line_number(i)]
        
        # 行番号ありは行番号順にソート
        issues_with_line.sort(key=lambda x: int(x.get("line", 0)))
        
        # 結合（行番号あり → 行番号なし）
        sorted_issues = issues_with_line + issues_without_line
        
        for issue in sorted_issues[:10]:  # 最大10件表示
            line_num = issue.get("line")
            issue_type = issue.get("type", 0)
            desc = issue.get("description", "")
            
            # 行番号の有無を判定
            has_line = has_line_number(issue)
            
            # タグ決定
            if issue_type in CRITICAL_TYPES:
                tag = "[CRITICAL] "
            elif not has_line:
                tag = "[MISSING] "
            else:
                tag = ""
            
            # 表示形式
            if has_line:
                prefix = f"  Line {line_num}: "
            else:
                prefix = f"  "
            
            # 長い説明は折り返し（55文字で分割）
            first_line_max = 55 - len(tag)
            if len(desc) > first_line_max:
                output_lines.append(f"{prefix}{tag}{desc[:first_line_max]}")
                # 残りを折り返し表示（インデント調整）
                indent = "           " if has_line else "            "
                remaining = desc[first_line_max:]
                while remaining:
                    output_lines.append(f"{indent}{remaining[:55]}")
                    remaining = remaining[55:]
            else:
                output_lines.append(f"{prefix}{tag}{desc}")
    else:
        output_lines.append("✓ Review: No issues")
    
    output_lines.append("")
    
    # Lint Errors
    lint_errors = details.get("lint_errors", [])
    lint_warnings = details.get("lint_warnings", [])
    
    if lint_errors:
        output_lines.append(f"⚠ Lint Errors ({len(lint_errors)}):")
        for err in lint_errors[:5]:  # 最大5件表示
            line_num = err.get("line", "?")
            message = err.get("message", "")
            rule = err.get("rule", "")
            rule_suffix = f" ({rule})" if rule else ""
            output_lines.append(f"  Line {line_num}: {message}{rule_suffix}")
    
    if lint_warnings:
        output_lines.append(f"⚠ Lint Warnings ({len(lint_warnings)}):")
        for warn in lint_warnings[:5]:  # 最大5件表示
            line_num = warn.get("line", "?")
            message = warn.get("message", "")
            rule = warn.get("rule", "")
            rule_suffix = f" ({rule})" if rule else ""
            output_lines.append(f"  Line {line_num}: {message}{rule_suffix}")
    
    if not lint_errors and not lint_warnings:
        output_lines.append("✓ Lint: No errors")
    
    output_lines.append(separator)
    
    return "\n".join(output_lines)

from typing import Dict, List, Optional, Set, NamedTuple
from pathlib import Path
import re

class FileRequirement(NamedTuple):
    """ファイル要件の定義"""
    filepath: str           # ファイルパス
    file_type: str         # 'html', 'js', 'css', 'python' など
    reason: str            # 必要な理由
    priority: str          # 'required', 'optional'


class ValidationResult(NamedTuple):
    """検証結果"""
    is_complete: bool                    # 完全か
    missing_files: List[str]             # 不足ファイルパス
    missing_requirements: List[FileRequirement]  # 不足要件
    reasons: List[str]                   # 不足理由


class PostGenerationValidator:
    """
    事後検証クラス
    生成されたファイルが要件を満たしているか検証し、
    不足している場合は追加生成を実行
    """
    
    def __init__(self, llm_manager, repository_analyzer, logger):
        """
        Args:
            llm_manager: LLMマネージャー
            repository_analyzer: リポジトリアナライザー
            logger: ロガー
        """
        self.llm_manager = llm_manager
        self.repository_analyzer = repository_analyzer
        self.logger = logger
        self.no_framework_mode = False  # 🆕 フレームワーク禁止モード
    
    def validate_and_complete(
        self,
        generated_files: Dict[str, str],
        goal: str,
        multi_file_detection: dict
    ) -> Dict[str, str]:
        """
        生成されたファイルを検証し、不足があれば補完
        
        Args:
            generated_files: LLMが生成したファイル群 {filename: code}
            goal: ユーザー目標
            multi_file_detection: 複数ファイル検出結果
        
        Returns:
            完全なファイル群 (必要に応じて追加生成されたファイルを含む)
        """
        # 複数ファイル不要の場合はスキップ
        if not multi_file_detection.get('required', False):
            self.logger.debug("[Post-Validation] Multi-file not required, skipping validation")
            return generated_files
        
        self.logger.debug("[Post-Validation] Starting post-generation validation...")
        
        # ステップ1: 期待されるファイルを予測
        expected_files = self._predict_required_files(
            goal=goal,
            multi_file_detection=multi_file_detection
        )
        
        if not expected_files:
            self.logger.debug("[Post-Validation] No expected files predicted, skipping validation")
            return generated_files
        
        self.logger.debug(f"[Post-Validation] Expected files: {[f.filepath for f in expected_files]}")
        self.logger.debug(f"[Post-Validation] Generated files: {list(generated_files.keys())}")
        
        # ステップ2: 完全性を検証
        validation_result = self._validate_completeness(
            generated_files=generated_files,
            expected_files=expected_files,
            goal=goal
        )
        
        if validation_result.is_complete:
            self.logger.debug("[Post-Validation] ✅ All required files generated")
            return generated_files
        
        # ステップ3: 不足ファイルを追加生成
        self.logger.debug(f"[Post-Validation] ⚠  Incomplete generation detected")
        self.logger.debug(f"[Post-Validation] Missing files: {validation_result.missing_files}")
        self.logger.debug(f"[Post-Validation] Reasons: {validation_result.reasons}")
        
        completed_files = self._generate_missing_files(
            generated_files=generated_files,
            missing_requirements=validation_result.missing_requirements,
            goal=goal
        )
        
        
        # ==========================================
        # 【新規追加】Import依存関係の検証（対策4）
        # ==========================================
        self.logger.debug("[Post-Validation] Validating import dependencies...")
        
        missing_imports = self._validate_import_dependencies(completed_files)
        
        if missing_imports:
            self.logger.debug(f"[Post-Validation] ⚠  Missing {len(missing_imports)} imported files")
            self.logger.debug(f"[Post-Validation] Missing imports: {[req.filepath for req in missing_imports]}")
            
            # 欠落したファイルを生成
            completed_files = self._generate_missing_files(
                generated_files=completed_files,
                missing_requirements=missing_imports,
                goal=goal
            )
            
            self.logger.debug(f"[Post-Validation] Import validation completed: {len(completed_files)} total files")
        else:
            self.logger.debug("[Post-Validation] ✅ All imports are satisfied")
        
        # 🆕 requirements.txt 互換性チェック
        self.logger.debug("[Post-Validation] Checking requirements.txt compatibility...")
        requirements_check = self._validate_requirements_compatibility(completed_files)
        
        if requirements_check['has_issues']:
            self.logger.warning(f"[Post-Validation] ⚠️ Requirements compatibility issues found:")
            for issue in requirements_check['issues']:
                self.logger.warning(f"[Post-Validation]   - {issue}")
            
            # 修正版で置き換え
            if requirements_check['fixed_content']:
                for filepath in list(completed_files.keys()):
                    if filepath.endswith('requirements.txt'):
                        self.logger.info(f"[Post-Validation] 🔧 Auto-fixing {filepath} for Python compatibility")
                        completed_files[filepath] = requirements_check['fixed_content']
                        break
        else:
            self.logger.debug("[Post-Validation] ✅ Requirements.txt compatibility OK")
        
        return completed_files
    
    def _validate_import_dependencies(self, generated_files: Dict[str, str]) -> List[FileRequirement]:
        """
        生成されたファイルのimport文を解析し、欠落したファイルを検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            欠落したファイルの FileRequirement リスト
        
        Examples:
            # JavaScript: src/main.jsx が以下をインポート:
            import { store } from "./store/index.js"
            # しかし store/index.js が生成されていない場合:
            → [FileRequirement(filepath='src/store/index.js', ...)] を返す
            
            # Python: main.py が以下をインポート:
            from services import ProductService
            # しかし services/ が生成されていない場合:
            → [FileRequirement(filepath='services/__init__.py', ...)] を返す
        """
        import os
        
        missing_requirements = []
        
        self.logger.debug("[Import Validation] Analyzing import dependencies...")
        
        # 生成されたファイルからプロジェクト内モジュールを抽出
        generated_modules = self._extract_python_modules(generated_files)
        self.logger.debug(f"[Import Validation] Generated Python modules: {generated_modules}")
        
        for filepath, content in generated_files.items():
            # ==========================================
            # Python ファイルのインポート検証
            # ==========================================
            if filepath.endswith('.py'):
                missing_requirements.extend(
                    self._validate_python_imports(filepath, content, generated_files, generated_modules)
                )
                continue
            
            # ==========================================
            # HTML ファイルのリソース参照検証
            # ==========================================
            if filepath.endswith(('.html', '.htm')):
                missing_requirements.extend(
                    self._validate_html_resources(filepath, content, generated_files)
                )
                continue
            
            # ==========================================
            # JavaScript/TypeScript/JSX ファイルのインポート検証
            # ==========================================
            if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                continue
            
            # import文を抽出
            # パターン1: import { X } from "path"
            # パターン2: import X from "path"
            # パターン3: import "path"
            import_pattern = r"import\s+(?:.*?\s+from\s+)?[\"'](.+?)[\"']"
            imports = re.findall(import_pattern, content)
            
            for import_path in imports:
                # node_modules や 絶対パスのインポートはスキップ
                if not (import_path.startswith('./') or import_path.startswith('../')):
                    continue
                
                # 相対パスを絶対パスに変換
                file_dir = os.path.dirname(filepath)
                
                # パスを正規化
                if import_path.startswith('./'):
                    resolved_path = os.path.join(file_dir, import_path[2:])
                elif import_path.startswith('../'):
                    resolved_path = os.path.join(file_dir, import_path)
                else:
                    resolved_path = import_path
                
                # パスを正規化（../ を解決）
                resolved_path = os.path.normpath(resolved_path)
                
                # Windowsパス区切りをUnix形式に変換
                resolved_path = resolved_path.replace('\\', '/')
                
                # 拡張子の補完
                if not resolved_path.endswith(('.js', '.jsx', '.ts', '.tsx', '.json', '.css')):
                    # 拡張子なしの場合、複数の候補を試す
                    candidates = [
                        resolved_path + '.js',
                        resolved_path + '.jsx',
                        resolved_path + '.ts',
                        resolved_path + '.tsx',
                        resolved_path + '/index.js',
                        resolved_path + '/index.jsx',
                    ]
                    
                    found = False
                    for candidate in candidates:
                        if candidate in generated_files:
                            found = True
                            break
                    
                    if not found:
                        # 最も一般的な .js を使用
                        missing_path = resolved_path + '.js'
                        # 重複チェック
                        if not any(req.filepath == missing_path for req in missing_requirements):
                            missing_requirements.append(FileRequirement(
                                filepath=missing_path,
                                file_type='js',
                                reason=f'Imported by {filepath}',
                                priority='required'
                            ))
                            self.logger.debug(f"[Import Validation] Missing: {missing_path} (imported from {filepath})")
                else:
                    # 拡張子ありの場合、そのまま確認
                    if resolved_path not in generated_files:
                        # 重複チェック
                        if not any(req.filepath == resolved_path for req in missing_requirements):
                            file_type = resolved_path.split('.')[-1]
                            if file_type not in ['js', 'jsx', 'ts', 'tsx', 'json', 'css']:
                                file_type = 'js'  # デフォルト
                            
                            missing_requirements.append(FileRequirement(
                                filepath=resolved_path,
                                file_type=file_type,
                                reason=f'Imported by {filepath}',
                                priority='required'
                            ))
                            self.logger.debug(f"[Import Validation] Missing: {resolved_path} (imported from {filepath})")
        
        self.logger.debug(f"[Import Validation] Found {len(missing_requirements)} missing imports")
        return missing_requirements
    
    def _extract_python_modules(self, generated_files: Dict[str, str]) -> set:
        """
        生成されたPythonファイルからモジュール名を抽出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            モジュール名のセット (例: {'models', 'views', 'services', 'config'})
        """
        modules = set()
        
        for filepath in generated_files.keys():
            if not filepath.endswith('.py'):
                continue
            
            # パスからモジュール名を抽出
            # 例: "models/product.py" → "models"
            # 例: "services/__init__.py" → "services"
            # 例: "main.py" → "main"
            parts = filepath.replace('\\', '/').split('/')
            
            if len(parts) >= 2:
                # ディレクトリがある場合、最初のディレクトリをモジュールとして登録
                modules.add(parts[0])
            
            # ファイル名（拡張子なし）もモジュールとして登録
            filename = parts[-1].replace('.py', '')
            if filename != '__init__':
                modules.add(filename)
        
        return modules
    
    def _validate_html_resources(
        self,
        filepath: str,
        content: str,
        generated_files: Dict[str, str]
    ) -> List[FileRequirement]:
        """
        HTMLファイルのリソース参照（CSS, JavaScript）を解析し、欠落したファイルを検出
        
        Args:
            filepath: HTMLファイルのパス
            content: HTMLファイルの内容
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            欠落したファイルの FileRequirement リスト
        
        Examples:
            # HTML: index.html が以下を参照:
            <link rel="stylesheet" href="css/styles.css">
            <script src="js/app.js"></script>
            # しかし css/styles.css が生成されていない場合:
            → [FileRequirement(filepath='css/styles.css', ...)] を返す
        """
        import os
        import re
        
        missing = []
        file_dir = os.path.dirname(filepath)
        
        self.logger.debug(f"[HTML Resource Validation] Analyzing: {filepath}")
        
        # ==========================================
        # CSS参照の検出: <link rel="stylesheet" href="...">
        # ==========================================
        # パターン: <link rel="stylesheet" href="path">
        # パターン: <link href="path" rel="stylesheet">
        css_patterns = [
            r'<link[^>]+rel=["\']stylesheet["\'][^>]+href=["\']([^"\']+)["\']',
            r'<link[^>]+href=["\']([^"\']+)["\'][^>]+rel=["\']stylesheet["\']',
            r'<link[^>]+href=["\']([^"\']+\.css)["\']'
        ]
        
        css_refs = set()
        for pattern in css_patterns:
            matches = re.findall(pattern, content, re.IGNORECASE)
            css_refs.update(matches)
        
        for css_path in css_refs:
            # 外部URL（http://, https://, //）はスキップ
            if css_path.startswith(('http://', 'https://', '//', 'data:')):
                continue
            
            # CDN参照はスキップ
            if 'cdn' in css_path.lower() or 'unpkg' in css_path.lower() or 'jsdelivr' in css_path.lower():
                continue
            
            # テンプレート構文（Jinja2/Django/ERB/PHP）はスキップ
            # これらはサーバーサイドで動的に解決されるパスであり、静的ファイルとして検証すべきではない
            if '{{' in css_path or '{%' in css_path or '<%' in css_path or '<?' in css_path:
                continue
            
            # 相対パスを解決
            if css_path.startswith('/'):
                # 絶対パス（ルートからの相対）
                resolved_path = css_path.lstrip('/')
            elif css_path.startswith('./'):
                resolved_path = os.path.join(file_dir, css_path[2:])
            elif css_path.startswith('../'):
                resolved_path = os.path.join(file_dir, css_path)
            else:
                # 相対パス
                if file_dir:
                    resolved_path = os.path.join(file_dir, css_path)
                else:
                    resolved_path = css_path
            
            # パスを正規化
            resolved_path = os.path.normpath(resolved_path).replace('\\', '/')
            
            # 生成ファイルに存在するか確認
            if resolved_path not in generated_files:
                # static/ プレフィックス付きも確認
                alt_path = f"static/{resolved_path}"
                if alt_path not in generated_files:
                    # 重複チェック
                    if not any(req.filepath == resolved_path for req in missing):
                        missing.append(FileRequirement(
                            filepath=resolved_path,
                            file_type='css',
                            reason=f'Referenced by {filepath}: <link href="{css_path}">',
                            priority='required'
                        ))
                        self.logger.debug(f"[HTML Resource Validation] Missing CSS: {resolved_path}")
        
        # ==========================================
        # JavaScript参照の検出: <script src="...">
        # ==========================================
        js_pattern = r'<script[^>]+src=["\']([^"\']+)["\']'
        js_refs = re.findall(js_pattern, content, re.IGNORECASE)
        
        for js_path in js_refs:
            # 外部URL（http://, https://, //）はスキップ
            if js_path.startswith(('http://', 'https://', '//', 'data:')):
                continue
            
            # CDN参照はスキップ
            if 'cdn' in js_path.lower() or 'unpkg' in js_path.lower() or 'jsdelivr' in js_path.lower():
                continue
            
            # テンプレート構文（Jinja2/Django/ERB/PHP）はスキップ
            # これらはサーバーサイドで動的に解決されるパスであり、静的ファイルとして検証すべきではない
            if '{{' in js_path or '{%' in js_path or '<%' in js_path or '<?' in js_path:
                continue
            
            # 相対パスを解決
            if js_path.startswith('/'):
                resolved_path = js_path.lstrip('/')
            elif js_path.startswith('./'):
                resolved_path = os.path.join(file_dir, js_path[2:])
            elif js_path.startswith('../'):
                resolved_path = os.path.join(file_dir, js_path)
            else:
                if file_dir:
                    resolved_path = os.path.join(file_dir, js_path)
                else:
                    resolved_path = js_path
            
            # パスを正規化
            resolved_path = os.path.normpath(resolved_path).replace('\\', '/')
            
            # 生成ファイルに存在するか確認
            if resolved_path not in generated_files:
                # static/ プレフィックス付きも確認
                alt_path = f"static/{resolved_path}"
                if alt_path not in generated_files:
                    # 重複チェック
                    if not any(req.filepath == resolved_path for req in missing):
                        missing.append(FileRequirement(
                            filepath=resolved_path,
                            file_type='js',
                            reason=f'Referenced by {filepath}: <script src="{js_path}">',
                            priority='required'
                        ))
                        self.logger.debug(f"[HTML Resource Validation] Missing JS: {resolved_path}")
        
        self.logger.debug(f"[HTML Resource Validation] Found {len(missing)} missing resources in {filepath}")
        return missing
    
    def _validate_python_imports(
        self, 
        filepath: str, 
        content: str, 
        generated_files: Dict[str, str],
        generated_modules: set
    ) -> List[FileRequirement]:
        """
        Pythonファイルのimport文を解析し、欠落したモジュールを検出
        
        Args:
            filepath: 検証対象のファイルパス
            content: ファイル内容
            generated_files: 生成されたファイル全体
            generated_modules: 生成されたモジュール名のセット
        
        Returns:
            欠落したファイルの FileRequirement リスト
        """
        missing = []
        
        # Python標準ライブラリ（主要なもの）
        STDLIB_MODULES = {
            'os', 'sys', 're', 'json', 'typing', 'datetime', 'time', 'math',
            'collections', 'itertools', 'functools', 'pathlib', 'logging',
            'unittest', 'abc', 'dataclasses', 'enum', 'copy',
            'hashlib', 'uuid', 'random', 'string', 'io', 'contextlib',
            'threading', 'multiprocessing', 'asyncio', 'concurrent',
            'http', 'urllib', 'socket', 'email', 'html', 'xml',
            'csv', 'sqlite3', 'pickle', 'shelve', 'dbm',
            'importlib', 'inspect', 'traceback', 'warnings', 'argparse',
            'configparser', 'shutil', 'tempfile', 'glob', 'fnmatch',
            'base64', 'binascii', 'struct', 'codecs', 'unicodedata',
            'locale', 'gettext', 'calendar', 'zoneinfo',
            'decimal', 'fractions', 'statistics', 'cmath',
            'array', 'bisect', 'heapq', 'queue', 'types', 'weakref',
            'textwrap', 'difflib', 'pprint', 'reprlib',
            'operator', 'keyword', 'token', 'tokenize', 'ast',
            'dis', 'code', 'codeop', 'compileall', 'py_compile',
            'zipfile', 'tarfile', 'gzip', 'bz2', 'lzma', 'zlib',
            'secrets', 'hmac', 'ssl',
            'subprocess', 'sched', 'signal', 'mmap', 'ctypes', 'platform',
            '__future__', 'builtins', 'atexit', 'gc', 'resource',
        }
        
        # よく使われるサードパーティパッケージ
        THIRD_PARTY_PACKAGES = {
            # Web Frameworks
            'flask', 'flask_sqlalchemy', 'flask_cors', 'flask_login', 'flask_wtf',
            'flask_migrate', 'flask_restful', 'flask_jwt_extended', 'flask_mail',
            # ⭐ Flask Extensions (追加)
            'flask_bcrypt', 'flask_session', 'flask_socketio', 'flask_caching',
            'flask_limiter', 'flask_admin', 'flask_babel', 'flask_security',
            'flask_marshmallow', 'flask_debugtoolbar', 'flask_principal',
            'flask_compress', 'flask_talisman', 'flask_moment', 'flask_assets',
            'django', 'fastapi', 'starlette', 'uvicorn', 'gunicorn', 'werkzeug',
            'bottle', 'tornado', 'aiohttp', 'sanic', 'quart', 'falcon',
            # ⭐ Django Extensions (追加)
            'rest_framework', 'djangorestframework', 'django_cors_headers',
            'django_filter', 'django_extensions', 'django_debug_toolbar',
            'django_allauth', 'django_celery_beat', 'django_redis',
            'django_storages', 'django_crispy_forms', 'crispy_forms',
            # Database
            'sqlalchemy', 'alembic', 'pymysql', 'psycopg2', 'pymongo', 'redis',
            'peewee', 'tortoise', 'databases', 'motor', 'asyncpg', 'aiomysql',
            # ⭐ Database Extensions (追加)
            'sqlmodel', 'mongoengine', 'elasticsearch', 'elasticsearch_dsl', 'cassandra',
            # Data Validation
            'marshmallow', 'pydantic', 'cerberus', 'voluptuous', 'attrs',
            # HTTP/API
            'requests', 'httpx', 'aiohttp', 'urllib3', 'httplib2',
            # ⭐ GraphQL (追加)
            'graphene', 'graphene_django', 'strawberry', 'ariadne',
            # Data Science
            'numpy', 'pandas', 'scipy', 'matplotlib', 'seaborn', 'plotly',
            'sklearn', 'scikit_learn', 'tensorflow', 'torch', 'keras',
            # Testing
            'pytest', 'pytest_flask', 'pytest_cov', 'unittest', 'mock', 'faker',
            'hypothesis', 'coverage', 'tox', 'nox',
            # ⭐ Testing Extensions (追加)
            'pytest_asyncio', 'pytest_django', 'pytest_mock', 'responses',
            'vcrpy', 'factory_boy',
            # Utilities
            'python_dotenv', 'dotenv', 'click', 'typer', 'rich', 'colorama',
            'pyyaml', 'yaml', 'toml', 'tomllib', 'configparser',
            'celery', 'rq', 'apscheduler', 'schedule',
            'pillow', 'PIL', 'opencv', 'cv2',
            'jinja2', 'mako', 'chameleon',
            'boto3', 'botocore', 'google', 'azure',
            'jwt', 'pyjwt', 'passlib', 'bcrypt', 'cryptography',
            'beautifulsoup4', 'bs4', 'lxml', 'scrapy', 'selenium',
            'paramiko', 'fabric', 'invoke',
            'loguru', 'structlog',
            'tenacity', 'backoff', 'retrying',
            # ⭐ Authentication (追加)
            'authlib', 'python_jose', 'jose', 'oauthlib',
            # ⭐ WebSocket (追加)
            'websockets', 'python_socketio', 'socketio', 'channels',
            # ⭐ Date/Time (追加)
            'arrow', 'pendulum', 'python_dateutil', 'dateutil',
            # ⭐ Configuration (追加)
            'dynaconf', 'python_decouple', 'decouple', 'environs',
            # ⭐ File Processing (追加)
            'openpyxl', 'xlrd', 'xlwt', 'python_docx', 'docx',
            'pypdf2', 'pypdf', 'reportlab', 'python_pptx', 'pptx',
            # ⭐ Serialization (追加)
            'orjson', 'ujson', 'simplejson',
            # ⭐ Caching (追加)
            'cachetools', 'diskcache', 'aiocache',
            # ⭐ Monitoring (追加)
            'sentry_sdk', 'prometheus_client', 'opentelemetry', 'newrelic', 'ddtrace',
            # ⭐ Message Queue (追加)
            'pika', 'kafka_python', 'confluent_kafka', 'kombu',
            # ⭐ Async (追加)
            'anyio', 'trio', 'httpcore',
            # ⭐ CLI (追加)
            'fire', 'argcomplete',
            # ⭐ Other (追加)
            'imageio', 'dramatiq', 'huey', 'minio', 's3fs', 'python_json_logger',
            'certifi', 'email_validator',
            # Type checking
            'mypy', 'pyright', 'typeguard',
        }
        
        # パターン1: from X import Y
        from_import_pattern = r'^from\s+([\w.]+)\s+import'
        # パターン2: import X
        import_pattern = r'^import\s+([\w.]+)'
        
        lines = content.split('\n')
        
        for line in lines:
            line = line.strip()
            
            # コメント行をスキップ
            if line.startswith('#'):
                continue
            
            module_name = None
            full_module_path = None
            
            # パターン1: from X import Y または from X.Y import Z
            match = re.match(from_import_pattern, line)
            if match:
                full_module_path = match.group(1)  # 完全なモジュールパス（例: services.user_service）
                module_name = full_module_path.split('.')[0]  # 最上位モジュール名
            
            # パターン2: import X または import X.Y
            if not module_name:
                match = re.match(import_pattern, line)
                if match:
                    full_module_path = match.group(1)
                    module_name = full_module_path.split('.')[0]
            
            if not module_name:
                continue
            
            # 相対インポートはスキップ（from . import X）
            if module_name == '':
                continue
            
            # 標準ライブラリはスキップ
            if module_name.lower() in STDLIB_MODULES:
                continue
            
            # サードパーティパッケージはスキップ
            if module_name.lower().replace('-', '_') in THIRD_PARTY_PACKAGES:
                continue
            
            # 既に生成されているモジュールはスキップ（最上位のみチェック）
            if module_name in generated_modules:
                # サブモジュールがある場合は追加チェック
                module_parts = full_module_path.split('.')
                if len(module_parts) > 1:
                    # サブモジュールのパス（例: services/user_service.py）
                    submodule_path = '/'.join(module_parts) + '.py'
                    
                    if submodule_path not in generated_files:
                        # サブモジュールが欠落
                        if not any(req.filepath == submodule_path for req in missing):
                            missing.append(FileRequirement(
                                filepath=submodule_path,
                                file_type='python',
                                reason=f'Imported by {filepath}: "{line.strip()}"',
                                priority='required'
                            ))
                            self.logger.debug(f"[Import Validation] Missing Python submodule: {submodule_path} (imported from {filepath})")
                continue
            
            # 生成されたファイルに直接存在するかチェック
            # 例: "services" → "services/__init__.py" または "services.py"
            possible_paths = [
                f"{module_name}/__init__.py",
                f"{module_name}.py",
            ]
            
            found = False
            for path in possible_paths:
                if path in generated_files:
                    found = True
                    break
            
            if not found:
                # 欠落モジュールとして登録（__init__.py）
                missing_path = f"{module_name}/__init__.py"
                
                # 重複チェック
                if not any(req.filepath == missing_path for req in missing):
                    missing.append(FileRequirement(
                        filepath=missing_path,
                        file_type='python',
                        reason=f'Imported by {filepath}: "{line.strip()}"',
                        priority='required'
                    ))
                    self.logger.debug(f"[Import Validation] Missing Python module: {module_name} (imported from {filepath})")
                
                # サブモジュールがある場合は、サブモジュールも登録
                module_parts = full_module_path.split('.')
                if len(module_parts) > 1:
                    submodule_path = '/'.join(module_parts) + '.py'
                    if not any(req.filepath == submodule_path for req in missing):
                        missing.append(FileRequirement(
                            filepath=submodule_path,
                            file_type='python',
                            reason=f'Imported by {filepath}: "{line.strip()}"',
                            priority='required'
                        ))
                        self.logger.debug(f"[Import Validation] Missing Python submodule: {submodule_path} (imported from {filepath})")
        
        return missing
    
    def _validate_requirements_compatibility(self, generated_files: Dict[str, str]) -> Dict[str, Any]:
        """
        requirements.txtのパッケージバージョンがPythonバージョンと互換性があるかチェック
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            {
                'has_issues': bool,
                'issues': List[str],
                'fixed_content': Optional[str]
            }
        """
        import sys
        import re
        
        result = {
            'has_issues': False,
            'issues': [],
            'fixed_content': None
        }
        
        # requirements.txt を探す
        requirements_content = None
        requirements_filepath = None
        for filepath, content in generated_files.items():
            if filepath.endswith('requirements.txt'):
                requirements_content = content
                requirements_filepath = filepath
                break
        
        if not requirements_content:
            self.logger.debug("[Requirements Validation] No requirements.txt found")
            return result
        
        python_version = (sys.version_info.major, sys.version_info.minor)
        python_version_str = f"{python_version[0]}.{python_version[1]}"
        self.logger.debug(f"[Requirements Validation] Checking compatibility for Python {python_version_str}")
        
        # configから互換性ルールを取得
        compatibility_rules = {}
        if self.config:
            python_compat = self.config.get("python_compatibility", {})
            rules = python_compat.get("rules", {})
            compatibility_rules = rules.get(python_version_str, {})
            self.logger.debug(f"[Requirements Validation] Loaded {len(compatibility_rules)} rules from config")
        
        if not compatibility_rules:
            self.logger.debug(f"[Requirements Validation] No compatibility rules for Python {python_version_str}")
            return result
        
        lines = requirements_content.split('\n')
        fixed_lines = []
        
        for line in lines:
            original_line = line
            line_stripped = line.strip()
            
            # コメント行や空行はスキップ
            if not line_stripped or line_stripped.startswith('#'):
                fixed_lines.append(original_line)
                continue
            
            # パッケージ名とバージョンを抽出
            # パターン: package==version, package>=version, package<=version, package~=version
            match = re.match(r'^([a-zA-Z0-9_-]+)\s*([=<>~!]+)\s*([0-9.]+)', line_stripped)
            
            if not match:
                fixed_lines.append(original_line)
                continue
            
            package_name_original = match.group(1)
            package_name = package_name_original.lower().replace('-', '_')
            operator = match.group(2)
            version = match.group(3)
            
            # 互換性問題があるかチェック（configのキーは-区切り）
            package_key = package_name_original.lower()
            rule = compatibility_rules.get(package_key)
            
            # _区切りでも検索
            if not rule:
                rule = compatibility_rules.get(package_name)
            
            if rule:
                min_version = rule.get('min_version')
                recommended = rule.get('recommended', min_version)
                reason = rule.get('reason', 'Compatibility issue')
                
                if min_version and operator == '==' and self._version_less_than(version, min_version):
                    result['has_issues'] = True
                    result['issues'].append(
                        f"{package_name_original}=={version} is incompatible with Python {python_version_str}: {reason}. "
                        f"Minimum required: {min_version}"
                    )
                    self.logger.warning(f"[Requirements Validation] ⚠️ {package_name_original}=={version} -> needs >={min_version}")
                    
                    # 修正版を生成（recommendedバージョンを使用）
                    fixed_line = f"{package_name_original}>={recommended}"
                    fixed_lines.append(fixed_line)
                    continue
            
            fixed_lines.append(original_line)
        
        if result['has_issues']:
            result['fixed_content'] = '\n'.join(fixed_lines)
            self.logger.info(f"[Requirements Validation] Found {len(result['issues'])} compatibility issue(s)")
        
        return result
    
    def _version_less_than(self, version1: str, version2: str) -> bool:
        """
        バージョン比較: version1 < version2 かどうか
        
        Args:
            version1: 比較元バージョン (例: "2.0.23")
            version2: 比較先バージョン (例: "2.0.25")
        
        Returns:
            version1 < version2 の場合 True
        """
        try:
            v1_parts = [int(x) for x in version1.split('.')]
            v2_parts = [int(x) for x in version2.split('.')]
            
            # 桁数を揃える
            while len(v1_parts) < len(v2_parts):
                v1_parts.append(0)
            while len(v2_parts) < len(v1_parts):
                v2_parts.append(0)
            
            return v1_parts < v2_parts
        except (ValueError, AttributeError):
            return False
    
    def _extract_explicit_filenames(self, goal: str) -> List[str]:
        """
        仕様書/ゴールから明示的に指定されたファイル名を抽出
        
        対応パターン:
        1. バッククォート: `filename.ext`
        2. File Output セクション: "- Single file: `neon_bubble_pop.html`"
        3. 出力指定: "Output: filename.ext", "Generate: filename.ext"
        4. ファイル名パターン: 行頭の "- filename.ext"
        
        Args:
            goal: ユーザー目標/仕様書テキスト
            
        Returns:
            検出されたファイル名のリスト（重複なし、検出順）
        """
        import re
        
        filenames = []
        
        # 有効な拡張子リスト
        VALID_EXTENSIONS = (
            'html', 'htm', 'css', 'js', 'jsx', 'ts', 'tsx',
            'py', 'json', 'yaml', 'yml', 'md', 'txt',
            'svg', 'xml'
        )
        ext_pattern = '|'.join(VALID_EXTENSIONS)
        
        # ============================================
        # パターン1（最優先）: File Output / Output file セクション
        # ============================================
        # "- Single file: `neon_bubble_pop.html`"
        # "## File Output\n- Single file: `filename.ext`"
        pattern1 = rf'(?:Single file|File Output|Output file|Output)[:\s]+[`"\']?([a-zA-Z0-9_\-\.]+\.(?:{ext_pattern}))[`"\']?'
        matches1 = re.findall(pattern1, goal, re.IGNORECASE)
        filenames.extend(matches1)
        
        # ============================================
        # パターン2: バッククォートで囲まれたファイル名
        # ============================================
        # `neon_bubble_pop.html`
        pattern2 = rf'`([a-zA-Z0-9_\-]+\.(?:{ext_pattern}))`'
        matches2 = re.findall(pattern2, goal, re.IGNORECASE)
        filenames.extend(matches2)
        
        # ============================================
        # パターン3: 生成指示
        # ============================================
        # "Generate: app.js", "Create: index.html"
        pattern3 = rf'(?:Generate|Create|Produce)[:\s]+[`"\']?([a-zA-Z0-9_\-\.]+\.(?:{ext_pattern}))[`"\']?'
        matches3 = re.findall(pattern3, goal, re.IGNORECASE)
        filenames.extend(matches3)
        
        # ============================================
        # パターン4: リスト項目のファイル名
        # ============================================
        # "- neon_bubble_pop.html"
        # "* index.js"
        pattern4 = rf'^[\-\*]\s+[`"\']?([a-zA-Z0-9_\-]+\.(?:{ext_pattern}))[`"\']?\s*$'
        matches4 = re.findall(pattern4, goal, re.MULTILINE | re.IGNORECASE)
        filenames.extend(matches4)
        
        # 重複除去（検出順を保持）
        seen = set()
        unique_filenames = []
        for fn in filenames:
            fn_lower = fn.lower()
            if fn_lower not in seen:
                seen.add(fn_lower)
                unique_filenames.append(fn)
        
        if unique_filenames:
            self.logger.debug(f"[Explicit Files] Detected: {unique_filenames}")
        
        return unique_filenames
    
    def _predict_required_files(
        self,
        goal: str,
        multi_file_detection: dict
    ) -> List[FileRequirement]:
        """
        Repository Mapと検出結果から期待されるファイルを予測
        
        Args:
            goal: ユーザー目標
            multi_file_detection: 複数ファイル検出結果
        
        Returns:
            期待されるファイル要件のリスト
        """
        requirements = []
        
        # ==========================================
        # 【最優先】Multi-File Detection の結果を使用
        # ==========================================
        
        detected_files = multi_file_detection.get("detected_files", [])
        if detected_files:
            self.logger.debug(f"[Post-Validation] Using detected files from Multi-File Detection: {detected_files}")
            
            for filename in detected_files:
                # 拡張子を取得
                ext = filename.split('.')[-1] if '.' in filename else 'unknown'
                
                requirements.append(FileRequirement(
                    filepath=filename,
                    file_type=ext,
                    reason=f'Explicitly mentioned in goal (detected by Multi-File Detection)',
                    priority='required'
                ))
            
            self.logger.debug(f"[Post-Validation] Predicted {len(requirements)} required files from Multi-File Detection")
            return requirements  # 明示的なファイルがあれば、ここで終了（慣習的推測をスキップ）
        
        # ==========================================
        # 【次優先】新規プロジェクトパターンの検出（対策1-A）
        # ==========================================
        goal_lower = goal.lower()
        
        # Reactプロジェクトの検出（明示的なフレームワーク指定のみ）
        react_keywords = [
            'react app', 'react application', 'create react',
            'react component', 'react project', 'using react',
            'with react', 'in react', 'react.js', 'reactjs'
        ]
        is_react_project = any(kw in goal_lower for kw in react_keywords)
        
        # 🆕 no_framework_modeの場合、React検出をスキップ
        if is_react_project and self.no_framework_mode:
            self.logger.debug("[Post-Validation] React keywords found but SKIPPED due to no-framework mode")
            is_react_project = False
        
        if is_react_project:
            self.logger.debug("[Post-Validation] React project detected from goal keywords")
            self.logger.debug(f"[Post-Validation] Goal: {goal[:100]}...")
            
            # React必須ファイルを追加
            # Note: .js と .jsx の両方をサポート（_validate_completeness が拡張子なしでマッチング）
            requirements.append(FileRequirement(
                filepath='src/App.js',  # または src/App.jsx
                file_type='js',
                reason='Main React component (REQUIRED for all React projects)',
                priority='required'
            ))
            
            requirements.append(FileRequirement(
                filepath='src/index.js',  # または src/index.jsx
                file_type='js',
                reason='React entry point',
                priority='required'
            ))
            
            # Vite: index.html, Create React App: public/index.html
            # どちらも検出されるように、public/index.htmlを優先
            requirements.append(FileRequirement(
                filepath='public/index.html',
                file_type='html',
                reason='HTML entry point (CRA) or index.html (Vite)',
                priority='required'
            ))
            
            self.logger.debug(f"[Post-Validation] Added {len(requirements)} React project requirements")
            self.logger.debug(f"[Post-Validation] Required files: {[r.filepath for r in requirements]}")
            return requirements
        
        # ==========================================
        # 【新規追加】明示的ファイル名の検出（最優先）
        # file_typesからのデフォルト推測より優先
        # ==========================================
        explicit_files = self._extract_explicit_filenames(goal)
        if explicit_files:
            self.logger.debug(f"[Post-Validation] Using explicit filenames from goal: {explicit_files}")
            
            for filename in explicit_files:
                ext = filename.split('.')[-1] if '.' in filename else 'unknown'
                requirements.append(FileRequirement(
                    filepath=filename,
                    file_type=ext,
                    reason=f'Explicitly specified in goal/spec',
                    priority='required'
                ))
            
            self.logger.debug(f"[Post-Validation] Predicted {len(requirements)} required files from explicit filenames")
            return requirements  # 明示的なファイルがあれば、デフォルト推測をスキップ
        
        # ==========================================
        # 【既存ロジック】file_types からの推測
        # Goal に明示的なファイル名がなく、React でもない場合、
        # file_types からデフォルトファイルを推測する
        # ==========================================
        file_types = multi_file_detection.get('file_types', [])
        if file_types and not requirements:
            self.logger.debug(f"[Post-Validation] Building requirements from file_types: {file_types}")
            
            # file_types に基づいてデフォルトファイルを追加
            default_files_for_types = {
                'html': ('index.html', 'html'),
                'css': ('styles.css', 'css'),
                'js': ('app.js', 'js'),
                'jsx': ('src/App.jsx', 'jsx'),
                'ts': ('src/main.ts', 'ts'),
                'tsx': ('src/App.tsx', 'tsx'),
                'python': ('main.py', 'py'),
            }
            
            for file_type in file_types:
                if file_type in default_files_for_types:
                    default_file, ext = default_files_for_types[file_type]
                    requirements.append(FileRequirement(
                        filepath=default_file,
                        file_type=ext,
                        reason=f"Required for web project (detected file_type: {file_type})",
                        priority='required'
                    ))
            
            if requirements:
                self.logger.debug(f"[Post-Validation] Predicted {len(requirements)} required files from file_types")
                self.logger.debug(f"[Post-Validation] Required files: {[r.filepath for r in requirements]}")
                return requirements
        
        # ==========================================
        # 【既存ロジック】Repository Analyzer依存
        # ==========================================
        if not self.repository_analyzer:
            self.logger.debug("[Post-Validation] No repository analyzer, cannot predict files")
            return requirements
        
        try:
            repo_data = self.repository_analyzer.memory.repository_data
            
            # goalから操作タイプを検出
            goal_lower = goal.lower()
            is_add_feature = any(kw in goal_lower for kw in ['add', 'create', 'implement', 'build'])
            is_modify = any(kw in goal_lower for kw in ['modify', 'update', 'change', 'extend', 'support'])
            
            # Repository Mapから既存ファイルを取得
            for filepath in repo_data.keys():
                file_type = self._get_file_type(filepath)
                
                if not file_type:
                    continue
                
                # HTML + JS プロジェクトの場合
                if file_type in ['html', 'js', 'css']:
                    # UI変更を伴う操作の場合、HTML/JS両方が必要
                    if is_add_feature or is_modify:
                        # HTML: UI要素が必要
                        if file_type == 'html':
                            requirements.append(FileRequirement(
                                filepath=filepath,
                                file_type='html',
                                reason='UI changes required (buttons, forms, or display elements)',
                                priority='required'
                            ))
                        
                        # JS: ロジックが必要
                        elif file_type == 'js':
                            requirements.append(FileRequirement(
                                filepath=filepath,
                                file_type='js',
                                reason='Logic implementation required (functions, event handlers)',
                                priority='required'
                            ))
                        
                        # CSS: スタイリングが必要な場合
                        elif file_type == 'css' and 'style' in goal_lower:
                            requirements.append(FileRequirement(
                                filepath=filepath,
                                file_type='css',
                                reason='Styling changes required',
                                priority='optional'
                            ))
                
                # Python複数ファイルプロジェクトの場合
                elif file_type == 'python':
                    # 機能追加の場合、関連する全てのPythonファイルが対象になりうる
                    if is_add_feature or is_modify:
                        requirements.append(FileRequirement(
                            filepath=filepath,
                            file_type='python',
                            reason='May require changes for feature implementation',
                            priority='optional'  # Pythonは明確な依存関係が必要
                        ))
            
            self.logger.debug(f"[Post-Validation] Predicted {len(requirements)} required files")
            
        except Exception as e:
            self.logger.debug(f"[Post-Validation] Failed to predict required files: {e}")
            import traceback
            self.logger.debug(traceback.format_exc())
        
        return requirements
    
    def _get_file_type(self, filepath: str) -> Optional[str]:
        """
        ファイルパスからファイルタイプを取得
        
        Args:
            filepath: ファイルパス
        
        Returns:
            'html', 'js', 'css', 'python' など、または None
        """
        if filepath.endswith(('.html', '.htm')):
            return 'html'
        elif filepath.endswith(('.js', '.jsx')):
            return 'js'
        elif filepath.endswith(('.css', '.scss', '.sass')):
            return 'css'
        elif filepath.endswith('.py'):
            return 'python'
        elif filepath.endswith(('.ts', '.tsx')):
            return 'typescript'
        elif filepath.endswith('.java'):
            return 'java'
        else:
            return None

    def _validate_completeness(
        self,
        generated_files: Dict[str, str],
        expected_files: List[FileRequirement],
        goal: str
    ) -> ValidationResult:
        """生成されたファイルが期待される要件を満たしているか検証"""
        generated_filenames = set(generated_files.keys())
        missing_files = []
        missing_requirements = []
        reasons = []
        
        for requirement in expected_files:
            filepath = requirement.filepath
            file_found = False
            
            if filepath in generated_filenames:
                file_found = True
            else:
                base_name = filepath.rsplit('.', 1)[0] if '.' in filepath else filepath
                for gen_file in generated_filenames:
                    gen_base = gen_file.rsplit('.', 1)[0] if '.' in gen_file else gen_file
                    if base_name == gen_base:
                        file_found = True
                        break
            
            if not file_found:
                missing_files.append(filepath)
                missing_requirements.append(requirement)
                reasons.append(f"{filepath}: {requirement.reason}")
        
        is_complete = len(missing_files) == 0
        
        return ValidationResult(
            is_complete=is_complete,
            missing_files=missing_files,
            missing_requirements=missing_requirements,
            reasons=reasons
        )
    
    
    def _extract_code_from_response(self, response_text: str, filepath: str) -> str:
        """
        LLM応答からコードを抽出する（マークダウンコードブロックを除去）
        
        対応するパターン:
        1. ```language\n...\n```  (標準)
        2. ```language\n...       (終わりがない - トークン制限で切れた場合)
        3. [コード]               (コードブロックなし)
        
        Args:
            response_text: LLMの応答テキスト
            filepath: ファイルパス（ログ用）
        
        Returns:
            抽出されたコード（不完全な場合は __INCOMPLETE__ プレフィックス付き）
        """
        # パターン1: 標準的なコードブロック ```language\n...\n```
        pattern1 = r'```(?:[a-z]+)?\s*\n(.*?)\n```'
        match1 = re.search(pattern1, response_text, re.DOTALL)
        if match1:
            self.logger.debug(f"[Code Extraction] ✅ Standard code block: {filepath}")
            return match1.group(1).strip()
        
        # パターン2: 開始のみのコードブロック ```language\n... (終わりがない)
        pattern2 = r'```(?:[a-z]+)?\s*\n(.*?)$'
        match2 = re.search(pattern2, response_text, re.DOTALL)
        if match2:
            self.logger.debug(f"[Code Extraction] ⚠️  Incomplete code block detected: {filepath}")
            self.logger.debug(f"  This file needs regeneration (LLM response was truncated)")
            # 不完全フラグを付けて返す
            extracted = match2.group(1).strip()
            return f"__INCOMPLETE__\n{extracted}"
        
        # パターン3: 先頭に ```language などがある場合は削除
        if response_text.strip().startswith('```'):
            lines = response_text.strip().split('\n')
            code = '\n'.join(lines[1:]).strip()
            self.logger.debug(f"[Code Extraction] ⚠  Removed markdown header: {filepath}")
            return code
        
        # それ以外はそのまま
        self.logger.debug(f"[Code Extraction] ℹ️  No code block markers: {filepath}")
        return response_text.strip()

    
    def _extract_embedded_javascript(self, html_content: str) -> str:
        """
        HTMLファイルから<script>タグ内のJavaScriptコードを抽出
        
        Args:
            html_content: HTMLファイルの内容
        
        Returns:
            抽出されたJavaScriptコード（複数の<script>タグがある場合は結合）
        """
        import re
        
        # <script>タグ内のコードを抽出（外部ファイル参照は除外）
        # (?![^>]*\bsrc\s*=) → srcアトリビュートがない<script>タグのみマッチ
        pattern = r'<script(?![^>]*\bsrc\s*=)[^>]*>(.*?)</script>'
        scripts = re.findall(pattern, html_content, re.DOTALL | re.IGNORECASE)
        
        if not scripts:
            return ""
        
        # 複数のスクリプトブロックを結合
        return '\n\n'.join(scripts)
    
    
    def _extract_embedded_css(self, html_content: str) -> str:
        """
        HTMLファイルから<style>タグ内のCSSコードを抽出
        
        Args:
            html_content: HTMLファイルの内容
        
        Returns:
            抽出されたCSSコード（複数の<style>タグがある場合は結合）
        """
        import re
        
        # <style>タグ内のコードを抽出
        pattern = r'<style[^>]*>(.*?)</style>'
        styles = re.findall(pattern, html_content, re.DOTALL | re.IGNORECASE)
        
        if not styles:
            return ""
        
        # 複数のスタイルブロックを結合
        return '\n\n'.join(styles)
    
    
    def _validate_html_tags(self, html_content: str) -> Tuple[bool, List[str]]:
        """
        HTMLタグの開閉一致をチェック
        
        Args:
            html_content: HTMLファイルの内容
        
        Returns:
            (is_valid, errors): 検証結果とエラーリスト
        """
        import re
        
        errors = []
        
        # チェック対象タグ
        critical_tags = ['html', 'head', 'body', 'script', 'style']
        
        for tag in critical_tags:
            # 開きタグと閉じタグの数を数える
            # (?i) = case-insensitive
            open_pattern = f'<{tag}(?:\\s|>)'
            close_pattern = f'</{tag}>'
            
            open_count = len(re.findall(open_pattern, html_content, re.IGNORECASE))
            close_count = len(re.findall(close_pattern, html_content, re.IGNORECASE))
            
            if open_count != close_count:
                errors.append(
                    f"Tag mismatch: <{tag}> found {open_count} times, "
                    f"</{tag}> found {close_count} times"
                )
        
        is_valid = len(errors) == 0
        
        if not is_valid:
            self.logger.debug(f"[HTML Tag Check] ❌ Tag mismatch detected")
            for error in errors:
                self.logger.debug(f"  - {error}")
        else:
            self.logger.debug(f"[HTML Tag Check] ✅ All tags matched")
        
        return is_valid, errors
    
    
    def _check_javascript_brackets(self, js_code: str) -> List[str]:
        """
        JavaScriptコードの括弧・ブレース・ブラケットの数をチェック
        
        Args:
            js_code: JavaScriptコード
        
        Returns:
            エラーリスト（エラーがなければ空リスト）
        """
        errors = []
        
        # 括弧のカウント
        open_braces = js_code.count('{')
        close_braces = js_code.count('}')
        if open_braces != close_braces:
            errors.append(f"Unmatched braces: {open_braces} open, {close_braces} close")
        
        open_parens = js_code.count('(')
        close_parens = js_code.count(')')
        if open_parens != close_parens:
            errors.append(f"Unmatched parentheses: {open_parens} open, {close_parens} close")
        
        open_brackets = js_code.count('[')
        close_brackets = js_code.count(']')
        if open_brackets != close_brackets:
            errors.append(f"Unmatched brackets: {open_brackets} open, {close_brackets} close")
        
        return errors
    
    
    def _validate_json_syntax(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        JSON構文チェック
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith('.json'):
            return True, ""
        
        try:
            import json
            json.loads(content)
            self.logger.debug(f"[JSON Syntax Check] ✅ Valid JSON: {filepath}")
            return True, ""
        except json.JSONDecodeError as e:
            error_msg = f"Line {e.lineno}, Column {e.colno}: {e.msg}"
            self.logger.debug(f"[JSON Syntax Check] ❌ Invalid JSON: {filepath}")
            self.logger.debug(f"  Error: {error_msg}")
            return False, error_msg
    
    
    def _validate_yaml_syntax(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        YAML構文チェック
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith(('.yaml', '.yml')):
            return True, ""
        
        try:
            import yaml
            yaml.safe_load(content)
            self.logger.debug(f"[YAML Syntax Check] ✅ Valid YAML: {filepath}")
            return True, ""
        except yaml.YAMLError as e:
            error_msg = str(e)
            self.logger.debug(f"[YAML Syntax Check] ❌ Invalid YAML: {filepath}")
            self.logger.debug(f"  Error: {error_msg}")
            return False, error_msg
        except Exception as e:
            # yamlモジュールが利用不可の場合
            self.logger.debug(f"[YAML Syntax Check] ⚠  YAML module not available, skipping: {filepath}")
            return True, ""
    
    
    def _validate_markdown_syntax(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        Markdown構文チェック（基本的なチェックのみ）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith(('.md', '.markdown')):
            return True, ""
        
        errors = []
        
        # コードブロックの開閉チェック
        import re
        code_blocks = re.findall(r'```', content)
        if len(code_blocks) % 2 != 0:
            errors.append("Unclosed code block (unmatched ```)")
        
        # リンク形式の基本チェック
        # [text](url) の形式
        link_pattern = r'\[([^\]]+)\]\(([^\)]*)\)'
        links = re.findall(link_pattern, content)
        for text, url in links:
            if not url.strip():
                errors.append(f"Empty link URL for text: [{text}]")
        
        if errors:
            error_msg = "; ".join(errors)
            self.logger.debug(f"[Markdown Syntax Check] ❌ Invalid Markdown: {filepath}")
            self.logger.debug(f"  Errors: {error_msg}")
            return False, error_msg
        
        self.logger.debug(f"[Markdown Syntax Check] ✅ Valid Markdown: {filepath}")
        return True, ""
    
    
    def _validate_xml_syntax(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        XML構文チェック（SVGを含む）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith(('.xml', '.svg')):
            return True, ""
        
        try:
            import xml.etree.ElementTree as ET
            ET.fromstring(content)
            self.logger.debug(f"[XML Syntax Check] ✅ Valid XML: {filepath}")
            return True, ""
        except ET.ParseError as e:
            error_msg = f"Line {e.position[0]}, Column {e.position[1]}: {e.msg}"
            self.logger.debug(f"[XML Syntax Check] ❌ Invalid XML: {filepath}")
            self.logger.debug(f"  Error: {error_msg}")
            return False, error_msg
        except Exception as e:
            error_msg = str(e)
            self.logger.debug(f"[XML Syntax Check] ❌ XML parse error: {filepath}")
            self.logger.debug(f"  Error: {error_msg}")
            return False, error_msg
    
    
    def _validate_naming_conventions(self, filepath: str, content: str) -> Tuple[bool, List[str]]:
        """
        命名規則チェック（基本的なパターンのみ）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, warnings): 検証結果と警告リスト
        """
        warnings = []
        
        # Python: snake_case チェック
        if filepath.endswith('.py'):
            import re
            # クラス定義: PascalCase推奨
            class_pattern = r'class\s+([a-z][a-z0-9_]*)\s*[:(]'
            lowercase_classes = re.findall(class_pattern, content)
            for class_name in lowercase_classes:
                warnings.append(f"Class name '{class_name}' should use PascalCase (e.g., {class_name.title()})")
            
            # 定数: UPPER_CASE推奨
            const_pattern = r'^([a-z][a-z0-9_]*)\s*=\s*["\'].*["\']'
            lowercase_consts = re.findall(const_pattern, content, re.MULTILINE)
            # 簡易チェック: 全て大文字のものがあれば定数と判断
            if re.search(r'^[A-Z][A-Z0-9_]*\s*=', content, re.MULTILINE):
                for const in lowercase_consts[:3]:  # 最初の3つのみ
                    if len(const) > 2 and const.upper() != const:
                        warnings.append(f"Consider using UPPER_CASE for constant: {const}")
        
        # JavaScript: camelCase チェック
        elif filepath.endswith(('.js', '.jsx')):
            import re
            # 関数定義: snake_case使用の検出
            func_pattern = r'function\s+([a-z][a-z0-9]*_[a-z0-9_]*)\s*\('
            snake_funcs = re.findall(func_pattern, content)
            for func_name in snake_funcs[:3]:
                camel_name = ''.join(word.capitalize() if i > 0 else word for i, word in enumerate(func_name.split('_')))
                warnings.append(f"Function name '{func_name}' should use camelCase (e.g., {camel_name})")
        
        # CSS: kebab-case チェック（class名）
        elif filepath.endswith('.css'):
            import re
            # class名にアンダースコアやPascalCase検出
            class_pattern = r'\.([A-Z][a-zA-Z0-9]*|[a-z][a-zA-Z0-9]*_[a-zA-Z0-9_]*)\s*\{'
            invalid_classes = re.findall(class_pattern, content)
            for class_name in invalid_classes[:3]:
                warnings.append(f"CSS class '.{class_name}' should use kebab-case")
        
        # 警告は失敗ではない
        is_valid = True
        
        if warnings:
            self.logger.debug(f"[Naming Convention Check] ⚠️  Style warnings for: {filepath}")
            for warning in warnings[:5]:  # 最初の5つのみログ
                self.logger.debug(f"  - {warning}")
        else:
            self.logger.debug(f"[Naming Convention Check] ✅ No style issues: {filepath}")
        
        return is_valid, warnings
    
    
    def _validate_encoding(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        エンコーディング検証（BOM付きUTF-8の検出）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 検証結果とエラーメッセージ
        """
        # BOM（Byte Order Mark）の検出
        if content.startswith('\ufeff'):
            error_msg = "File contains UTF-8 BOM (Byte Order Mark)"
            self.logger.debug(f"[Encoding Check] ⚠️  BOM detected: {filepath}")
            self.logger.debug(f"  {error_msg}")
            # BOMは警告レベル（失敗ではない）
            return True, error_msg
        
        self.logger.debug(f"[Encoding Check] ✅ No encoding issues: {filepath}")
        return True, ""
    
    
    def _validate_line_endings(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        改行コードの統一チェック（CRLF vs LF）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, warning_message): 検証結果と警告メッセージ
        """
        has_crlf = '\r\n' in content
        has_lf_only = '\n' in content.replace('\r\n', '')
        
        if has_crlf and has_lf_only:
            warning_msg = "Mixed line endings detected (CRLF and LF)"
            self.logger.debug(f"[Line Ending Check] ⚠️  Mixed line endings: {filepath}")
            self.logger.debug(f"  {warning_msg}")
            # 混在は警告レベル（失敗ではない）
            return True, warning_msg
        
        self.logger.debug(f"[Line Ending Check] ✅ Consistent line endings: {filepath}")
        return True, ""
    
    
    def _validate_css_syntax(self, filepath: str, content: str) -> Tuple[bool, str]:
        """
        CSS基本構文チェック（簡易版）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith('.css'):
            return True, ""
        
        errors = []
        
        # 基本的なブレース一致チェック
        open_braces = content.count('{')
        close_braces = content.count('}')
        if open_braces != close_braces:
            errors.append(f"Unmatched braces: {open_braces} open, {close_braces} close")
        
        # コメント閉じ忘れチェック
        import re
        # /* で始まり */ で終わらないコメント
        unclosed_comments = re.findall(r'/\*(?:(?!\*/).)*$', content, re.DOTALL | re.MULTILINE)
        if unclosed_comments:
            errors.append("Unclosed CSS comment (/* without */)")
        
        if errors:
            error_msg = "; ".join(errors)
            self.logger.debug(f"[CSS Syntax Check] ❌ Invalid CSS: {filepath}")
            self.logger.debug(f"  Errors: {error_msg}")
            return False, error_msg
        
        self.logger.debug(f"[CSS Syntax Check] ✅ Valid CSS: {filepath}")
        return True, ""
    
    
    def _validate_code_completeness(self, filepath: str, content: str) -> Tuple[bool, List[str]]:
        """
        コードの完全性をチェックする
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_complete, errors): 完全性の判定結果とエラーリスト
        """
        # HTMLファイルの場合は特別処理
        if filepath.endswith('.html'):
            errors = []
            
            # 1. HTMLタグの開閉チェック
            tags_valid, tag_errors = self._validate_html_tags(content)
            if not tags_valid:
                errors.extend(tag_errors)
            
            # 2. 埋め込みJavaScriptの抽出と検証
            js_code = self._extract_embedded_javascript(content)
            if js_code.strip():
                # JavaScriptとして括弧・ブレースのチェック
                js_errors = self._check_javascript_brackets(js_code)
                if js_errors:
                    errors.extend([f"Embedded JavaScript: {e}" for e in js_errors])
            
            # 3. 埋め込みCSSの抽出と検証
            css_code = self._extract_embedded_css(content)
            if css_code.strip():
                # CSSとしてブレースのチェック
                open_braces = css_code.count('{')
                close_braces = css_code.count('}')
                if open_braces != close_braces:
                    errors.append(f"Embedded CSS: Unmatched braces: {open_braces} open, {close_braces} close")
            
            # 4. マークダウン記号の残存チェック
            if content.strip().startswith('```'):
                errors.append("File starts with markdown code block marker")
            
            is_complete = len(errors) == 0
            
            if not is_complete:
                self.logger.debug(f"[Completeness Check] ❌ Incomplete HTML: {filepath}")
                for error in errors:
                    self.logger.debug(f"  - {error}")
            else:
                self.logger.debug(f"[Completeness Check] ✅ Complete HTML: {filepath}")
            
            return is_complete, errors
        
        # Pythonファイルの場合は特別処理
        if filepath.endswith('.py'):
            errors = []
            
            # 括弧・ブレース・ブラケットの数チェック
            open_braces = content.count('{')
            close_braces = content.count('}')
            if open_braces != close_braces:
                errors.append(f"Unmatched braces: {open_braces} open, {close_braces} close")
            
            open_parens = content.count('(')
            close_parens = content.count(')')
            if open_parens != close_parens:
                errors.append(f"Unmatched parentheses: {open_parens} open, {close_parens} close")
            
            open_brackets = content.count('[')
            close_brackets = content.count(']')
            if open_brackets != close_brackets:
                errors.append(f"Unmatched brackets: {open_brackets} open, {close_brackets} close")
            
            # 不完全な最終行のチェック
            lines = content.strip().split('\n')
            if lines:
                last_line = lines[-1].strip()
                incomplete_patterns = [
                    'def ', 'class ', 'import ', 'from ',
                    'if ', 'elif ', 'else:', 'for ', 'while ',
                    '= ', 'return ', 'raise ', 'assert ',
                    'try:', 'except ', 'finally:', 'with ',
                ]
                for pattern in incomplete_patterns:
                    if last_line.endswith(pattern) or last_line.endswith(pattern.rstrip()):
                        errors.append(f"Incomplete statement at end of file: '{last_line[-50:]}'")
                        break
            
            # マークダウン記号の残存チェック
            if content.strip().startswith('```'):
                errors.append("File starts with markdown code block marker")
            
            is_complete = len(errors) == 0
            
            if not is_complete:
                self.logger.debug(f"[Completeness Check] ❌ Incomplete Python: {filepath}")
                for error in errors:
                    self.logger.debug(f"  - {error}")
            else:
                self.logger.debug(f"[Completeness Check] ✅ Complete Python: {filepath}")
            
            return is_complete, errors
        
        # CSSファイルの場合は特別処理
        if filepath.endswith('.css'):
            errors = []
            
            # ブレースの数チェック
            open_braces = content.count('{')
            close_braces = content.count('}')
            if open_braces != close_braces:
                errors.append(f"Unmatched braces: {open_braces} open, {close_braces} close")
            
            # マークダウン記号の残存チェック
            if content.strip().startswith('```'):
                errors.append("File starts with markdown code block marker")
            
            is_complete = len(errors) == 0
            
            if not is_complete:
                self.logger.debug(f"[Completeness Check] ❌ Incomplete CSS: {filepath}")
                for error in errors:
                    self.logger.debug(f"  - {error}")
            else:
                self.logger.debug(f"[Completeness Check] ✅ Complete CSS: {filepath}")
            
            return is_complete, errors
        
        # JavaScript/TypeScriptファイルのチェック
        if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
            return True, []
        
        errors = []
        
        # 1. export default の存在チェック（App.js等の主要ファイル）
        if 'App.js' in filepath or 'App.jsx' in filepath or 'Main.js' in filepath or 'Main.jsx' in filepath:
            if 'export default' not in content and 'export {' not in content:
                errors.append("Missing 'export default' statement")
        
        # 2. 括弧の数チェック
        open_braces = content.count('{')
        close_braces = content.count('}')
        if open_braces != close_braces:
            errors.append(f"Unmatched braces: {open_braces} open, {close_braces} close")
        
        open_parens = content.count('(')
        close_parens = content.count(')')
        if open_parens != close_parens:
            errors.append(f"Unmatched parentheses: {open_parens} open, {close_parens} close")
        
        open_brackets = content.count('[')
        close_brackets = content.count(']')
        if open_brackets != close_brackets:
            errors.append(f"Unmatched brackets: {open_brackets} open, {close_brackets} close")
        
        # 3. 不完全な最終行のチェック
        lines = content.strip().split('\n')
        if lines:
            last_line = lines[-1].strip()
            incomplete_patterns = [
                'alt="', 'src="', 'href="',
                '<img ', '<a ', '<div ',
                'const ', 'let ', 'var ',
                'import ', 'from ',
                'function ', 'class ',
                '= {', '= [', '= (',
            ]
            for pattern in incomplete_patterns:
                if last_line.endswith(pattern) or last_line.endswith(pattern.rstrip()):
                    errors.append(f"Incomplete statement at end of file: '{last_line[-50:]}'")
                    break
        
        # 4. マークダウン記号の残存チェック
        if content.strip().startswith('```'):
            errors.append("File starts with markdown code block marker")
        
        is_complete = len(errors) == 0
        
        if not is_complete:
            self.logger.debug(f"[Completeness Check] ❌ Incomplete code: {filepath}")
            for error in errors:
                self.logger.debug(f"  - {error}")
        else:
            self.logger.debug(f"[Completeness Check] ✅ Complete code: {filepath}")
        
        return is_complete, errors

    
    def _validate_javascript_syntax(self, filepath: str, content: str) -> Tuple[bool, Optional[str]]:
        """
        JavaScript/JSXの構文チェック（Node.js使用）
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
        
        Returns:
            (is_valid, error_message): 構文の妥当性とエラーメッセージ
        """
        if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
            return True, None
        
        # Node.jsが利用可能か確認
        try:
            result = subprocess.run(
                ['node', '--version'],
                capture_output=True,
                timeout=2,
                encoding='utf-8',
                errors='replace'
            )
            if result.returncode != 0:
                self.logger.debug(f"[Syntax Check] ⚠  Node.js not available, skipping syntax check")
                return True, None
        except (FileNotFoundError, subprocess.TimeoutExpired):
            self.logger.debug(f"[Syntax Check] ⚠  Node.js not available, skipping syntax check")
            return True, None
        
        # 一時ファイルに書き込んで構文チェック
        import tempfile
        with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False, encoding='utf-8') as f:
            f.write(content)
            temp_path = f.name
        
        try:
            result = subprocess.run(
                ['node', '--check', temp_path],
                capture_output=True,
                timeout=5,
                encoding='utf-8',
                errors='replace'
            )
            
            if result.returncode != 0:
                error_message = result.stderr.strip()
                self.logger.debug(f"[Syntax Check] ❌ Syntax error: {filepath}")
                self.logger.debug(f"  Error: {error_message}")
                return False, error_message
            
            self.logger.debug(f"[Syntax Check] ✅ Valid syntax: {filepath}")
            return True, None
        
        except subprocess.TimeoutExpired:
            self.logger.debug(f"[Syntax Check] ⚠  Timeout during syntax check: {filepath}")
            return True, None
        
        finally:
            try:
                os.unlink(temp_path)
            except:
                pass

    def _generate_missing_files(
        self,
        generated_files: Dict[str, str],
        missing_requirements: List[FileRequirement],
        goal: str
    ) -> Dict[str, str]:
        """不足しているファイルを生成"""
        completed_files = generated_files.copy()
        
        if not missing_requirements:
            return completed_files
        
        self.logger.debug(f"[Post-Validation] Generating {len(missing_requirements)} missing files...")
        
        # 既存ファイルも含めた完全なコンテキストを構築
        existing_files_context = []
        
        # 既存ファイルを読み込む（元のファイルが存在する場合）
        for requirement in missing_requirements:
            existing_file_path = Path(requirement.filepath)
            if existing_file_path.exists():
                try:
                    with open(existing_file_path, 'r', encoding='utf-8') as f:
                        original_content = f.read()
                    existing_files_context.append(
                        f"## {requirement.filepath} (ORIGINAL VERSION - PRESERVE FUNCTIONALITY)\n```\n{original_content}\n```"
                    )
                except Exception as e:
                    self.logger.debug(f"[Post-Validation] Could not read existing file {requirement.filepath}: {e}")
        
        # 生成済みファイルを追加（全文）
        for filename, code in generated_files.items():
            existing_files_context.append(
                f"## {filename} (NEWLY GENERATED)\n```\n{code}\n```"
            )
        
        existing_context = "\n\n".join(existing_files_context)
        
        for requirement in missing_requirements:
            filepath = requirement.filepath
            file_type = requirement.file_type
            reason = requirement.reason
            max_retries = 2
            
            for attempt in range(max_retries):
                self.logger.debug(f"[Post-Validation] Generating: {filepath} (Attempt {attempt + 1}/{max_retries})")
                
            
                self.logger.debug(f"[Post-Validation] Generating: {filepath} (Reason: {reason})")
                self.logger.debug(f"[Post-Validation] Existing context length: {len(existing_context)} chars")
                self.logger.debug(f"[Post-Validation] Context includes {len(existing_files_context)} files")
            
                # 解決策2+3: ファイル名とReasonからフレームワークを判定
                is_react_file = (
                    filepath.startswith('src/') and 
                    filepath.endswith(('.js', '.jsx', '.tsx'))
                )
                is_react_project = 'React' in reason
                
                # 🆕 no_framework_modeの場合はReact指示をスキップ
                if self.no_framework_mode and is_react_project:
                    self.logger.debug(f"[Post-Validation] Skipping React instruction for {filepath} due to no-framework mode")
                    is_react_project = False
                
                # フレームワーク固有の指示を構築
                framework_instruction = ""
                if is_react_file and is_react_project:
                    self.logger.debug(f"[Post-Validation] Detected React file: {filepath}")
                    framework_instruction = """
⚠️ FRAMEWORK REQUIREMENT: REACT
This file MUST be created as a React component/module.

MANDATORY REACT STRUCTURE:
1. Import React at the top:
   - For components: import React from 'react';
   - For index.js: import React from 'react'; import ReactDOM from 'react-dom/client';

2. Create a React component:
   - Functional component (preferred): function App() { ... } or const App = () => { ... }
   - Class component: class App extends React.Component { ... }

3. Use JSX syntax for UI elements:
   - Return JSX: return <div>...</div>;
   - NOT vanilla DOM manipulation

4. Export the component:
   - export default ComponentName;

5. For index.js specifically:
   - Import the App component: import App from './App';
   - Use ReactDOM: const root = ReactDOM.createRoot(document.getElementById('root'));
   - Render: root.render(<App />);

⚠️ CRITICAL: DO NOT create vanilla JavaScript. This MUST be React code with imports and JSX.

"""
            
                completion_prompt = framework_instruction + f"""Generate the missing file for the project.

    ⚠ CRITICAL INSTRUCTIONS:
    1. An ORIGINAL version of this file may exist - you MUST preserve its functionality
    2. You are MODIFYING an existing file, NOT creating from scratch
    3. PRESERVE the existing code structure, event handlers, and method names
    4. ONLY ADD the new functionality requested in the goal
    5. DO NOT change existing working code unless absolutely necessary
    6. DO NOT introduce new method names that don't exist in JavaScript files
    7. CHECK that all method calls in HTML match method definitions in JavaScript

    Original Goal: {goal}

    Context - Existing and Generated Files:
    {existing_context}

    Missing/Incomplete File to Generate:
    - File: {filepath}
    - Type: {file_type}
    - Reason: {reason}

    REQUIREMENTS:
    1. Generate the COMPLETE file: {filepath}
    2. If an ORIGINAL version exists above, PRESERVE its structure and functionality
    3. Ensure ALL method calls in HTML match method definitions in JavaScript
    4. Use the SAME event handling approach as the original (e.g., data-action vs data-operation)
    5. Use the SAME data attribute style (e.g., data-number="7" not just data-number)
    6. Make it production-ready and complete
    7. DO NOT use method names that don't exist (check JavaScript class methods first)

    Generate the complete code for {filepath}:"""
            
                # 改善策B: ファイルタイプとコンテキスト長に応じてmax_tokensを動的設定
                # 
                # 問題: app.js（小文字）がLARGE_FILE_PATTERNSにマッチせず、
                #       max_tokens=4000では複雑なJSファイルが途中で切り詰められる
                # 
                # 解決策: 
                # 1. JS系ファイルは基本的に大きなmax_tokensを使用
                # 2. コンテキストが大きい場合は更に増やす
                # 3. 特に重要なファイルパターンは最大値を使用
                
                LARGE_FILE_PATTERNS = [
                    'App.js', 'App.jsx', 'App.tsx',
                    'app.js', 'app.jsx', 'app.tsx',  # 小文字も追加
                    'Main.js', 'Main.jsx', 'Main.tsx',
                    'main.js', 'main.jsx', 'main.tsx',  # 小文字も追加
                    'index.js', 'index.jsx', 'index.tsx',  # index.js も追加
                    'script.js', 'scripts.js',  # 一般的なJSファイル名
                    'calendar.js', 'todo.js', 'calculator.js',  # アプリ系
                ]
                
                # ファイルタイプに基づく基本max_tokens
                JS_FILE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx']
                is_js_file = any(filepath.endswith(ext) for ext in JS_FILE_EXTENSIONS)
                is_large_file_pattern = any(pattern in filepath for pattern in LARGE_FILE_PATTERNS)
                
                # コンテキストの長さに基づく判断
                context_length = len(existing_context)
                is_large_context = context_length > 10000  # 10KB以上
                
                # max_tokens の決定ロジック
                if is_large_file_pattern:
                    # 重要なファイルパターン → 最大値
                    max_tokens = 16000
                    self.logger.debug(f"[Post-Validation] Large file pattern detected: {filepath}, using max_tokens={max_tokens}")
                elif is_js_file and is_large_context:
                    # JS系 + 大きなコンテキスト → 大きな出力が必要
                    max_tokens = 16000
                    self.logger.debug(f"[Post-Validation] JS file with large context ({context_length} chars): {filepath}, using max_tokens={max_tokens}")
                elif is_js_file:
                    # JS系ファイル → 基本的に大きめ
                    max_tokens = 8000
                    self.logger.debug(f"[Post-Validation] JS file detected: {filepath}, using max_tokens={max_tokens}")
                else:
                    # その他のファイル
                    max_tokens = 4000
            
                try:
                    response = self.llm_manager.generate_response(
                        prompt=completion_prompt,
                        system_prompt="You are a code generation expert. Generate complete, production-ready code files.",
                        max_tokens=max_tokens
                    )
                
                    if hasattr(response, 'content'):
                        response_text = response.content
                    else:
                        response_text = str(response)
                
                    # 対策5-A: 改善されたコード抽出ロジック
                    file_code = self._extract_code_from_response(response_text, filepath)
                    
                    # 不完全フラグチェック
                    if file_code.startswith("__INCOMPLETE__"):
                        file_code = file_code.replace("__INCOMPLETE__\n", "", 1)
                        self.logger.debug(f"[Post-Validation] ⚠️  Incomplete code detected: {filepath}")
                        if attempt < max_retries - 1:
                            self.logger.debug(f"[Post-Validation] 🔄 Retrying due to incomplete code...")
                            continue  # 再試行
                        else:
                            self.logger.debug(f"[Post-Validation] ❌ Failed to generate complete code after {max_retries} attempts")
                            self.logger.debug(f"  Saving incomplete code anyway (manual review required)")
                            # 元のコードと同じ動作: 不完全でも保存して処理を継続
                    
                    completed_files[filepath] = file_code
                    
                    # 対策5-B: 構文チェック（ファイルタイプ別）
                    syntax_valid = True
                    syntax_error = None
                    
                    # Python構文チェック
                    if filepath.endswith('.py'):
                        syntax_valid, syntax_error = self._validate_python_syntax(file_code, filepath)
                    # JavaScript/TypeScript構文チェック
                    elif filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                        syntax_valid, syntax_error = self._validate_javascript_syntax(filepath, file_code)
                    # JSON構文チェック
                    elif filepath.endswith('.json'):
                        syntax_valid, syntax_error = self._validate_json_syntax(filepath, file_code)
                    # YAML構文チェック
                    elif filepath.endswith(('.yaml', '.yml')):
                        syntax_valid, syntax_error = self._validate_yaml_syntax(filepath, file_code)
                    # Markdown構文チェック
                    elif filepath.endswith(('.md', '.markdown')):
                        syntax_valid, syntax_error = self._validate_markdown_syntax(filepath, file_code)
                    # XML/SVG構文チェック
                    elif filepath.endswith(('.xml', '.svg')):
                        syntax_valid, syntax_error = self._validate_xml_syntax(filepath, file_code)
                    # CSS構文チェック
                    elif filepath.endswith('.css'):
                        syntax_valid, syntax_error = self._validate_css_syntax(filepath, file_code)
                    
                    # 対策5-C: 完全性チェック
                    complete_valid, completeness_errors = self._validate_code_completeness(filepath, file_code)
                    
                    # 対策5-D: 追加チェック（警告レベル）
                    # 命名規則チェック
                    naming_valid, naming_warnings = self._validate_naming_conventions(filepath, file_code)
                    if naming_warnings:
                        self.logger.debug(f"[Post-Validation] ⚠️  Style warnings: {filepath}")
                        for warning in naming_warnings[:3]:
                            self.logger.debug(f"    {warning}")
                    
                    # エンコーディングチェック
                    encoding_valid, encoding_msg = self._validate_encoding(filepath, file_code)
                    if encoding_msg:
                        self.logger.debug(f"[Post-Validation] ⚠️  Encoding: {encoding_msg}")
                    
                    # 改行コードチェック
                    lineend_valid, lineend_msg = self._validate_line_endings(filepath, file_code)
                    if lineend_msg:
                        self.logger.debug(f"[Post-Validation] ⚠️  Line endings: {lineend_msg}")
                    
                    # 検証結果に基づいて処理
                    if syntax_valid and complete_valid:
                        # ✅ 検証成功
                        self.logger.debug(f"[Post-Validation] ✅ Validation passed: {filepath}")
                        break  # 再試行ループを抜ける
                    else:
                        # ❌ 検証失敗
                        if attempt < max_retries - 1:
                            self.logger.debug(f"[Post-Validation] 🔄 Retrying generation for {filepath}...")
                            if not syntax_valid:
                                self.logger.debug(f"  Reason: Syntax error - {syntax_error}")
                            if not complete_valid:
                                self.logger.debug(f"  Reason: Incomplete code - {completeness_errors}")
                        else:
                            # 最終試行でも失敗 → それでも保存
                            self.logger.debug(f"[Post-Validation] ⚠  Failed validation after {max_retries} attempts: {filepath}")
                            self.logger.debug(f"  Saving anyway (manual review required)")
                
                except Exception as e:
                    self.logger.debug(f"[Post-Validation] ❌ Failed to generate {filepath}: {e}")
                    import traceback
                    self.logger.debug(traceback.format_exc())
        
        self.logger.debug(f"[Post-Validation] Completion finished: {len(completed_files)} total files")
        
        # 生成後の整合性チェック
        consistency_issues = self._validate_file_consistency(completed_files)
        if consistency_issues:
            self.logger.debug("[Post-Validation] ⚠  Consistency issues detected:")
            for issue in consistency_issues:
                self.logger.debug(f"   - {issue}")
        
        return completed_files
    
    def _validate_file_consistency(self, files: Dict[str, str]) -> List[str]:
        """
        生成されたファイル間の整合性をチェック
        
        Args:
            files: {filename: code}
        
        Returns:
            整合性の問題リスト
        """
        issues = []
        
        # ⭐ 修正B: 未実装メソッド検出（pass only）
        issues.extend(self._check_unimplemented_methods(files))
        
        # HTMLとJavaScriptのペアを探す
        html_files = {f: c for f, c in files.items() if f.endswith('.html')}
        js_files = {f: c for f, c in files.items() if f.endswith('.js')}
        
        for html_file, html_code in html_files.items():
            for js_file, js_code in js_files.items():
                # HTMLから呼び出されているメソッドを抽出
                method_calls = re.findall(r'calculator\.(\w+)\(', html_code)
                
                # JavaScriptで定義されているメソッドを抽出
                method_definitions = re.findall(r'^\s*(\w+)\s*\([^)]*\)\s*\{', js_code, re.MULTILINE)
                
                # 呼び出されているが定義されていないメソッドを検出
                undefined_methods = set(method_calls) - set(method_definitions)
                for method in undefined_methods:
                    issues.append(
                        f"{html_file}: Method 'calculator.{method}()' is called but not defined in {js_file}"
                    )
                
                # data属性の値チェック（data-number などが空でないか）
                empty_data_attrs = re.findall(r'data-(\w+)>(?!\w)', html_code)
                if empty_data_attrs:
                    for attr in set(empty_data_attrs):
                        issues.append(
                            f"{html_file}: data-{attr} attribute has no value (should be data-{attr}=\"value\")"
                        )
        
        return issues
    
    def _check_unimplemented_methods(self, files: Dict[str, str]) -> List[str]:
        """
        ⭐ 修正B: 未実装メソッド（pass only）を検出
        
        def method_name(...):
            pass
        
        または
        
        def method_name(...):
            '''docstring'''
            pass
        
        のパターンを検出し、警告を返す。
        
        Args:
            files: {filename: code}
        
        Returns:
            未実装メソッドの警告リスト
        """
        issues = []
        
        for filename, code in files.items():
            if not filename.endswith('.py'):
                continue
            
            # パターン1: def xxx(...): の後に pass のみ（docstringなし）
            # パターン2: def xxx(...): の後に docstring + pass
            # 
            # 正規表現で検出:
            # def メソッド名(引数): 
            #     (オプション: """docstring""" または '''docstring''')
            #     pass
            
            pattern = re.compile(
                r'def\s+(\w+)\s*\([^)]*\)\s*(?:->\s*[^:]+)?\s*:\s*\n'  # def xxx(...) -> type:
                r'(?:\s*(?:"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\')\s*\n)?'  # optional docstring
                r'\s*pass\s*(?:\n|$)',  # pass
                re.MULTILINE
            )
            
            matches = pattern.findall(code)
            
            for method_name in matches:
                # __init__ や __repr__ などの特殊メソッドで pass のみは許容
                if method_name.startswith('__') and method_name.endswith('__'):
                    continue
                
                issues.append(
                    f"{filename}: Method '{method_name}()' is not implemented (pass only)"
                )
                logger.debug(f"[Consistency] Unimplemented method detected: {filename}:{method_name}()")
        
        return issues


@dataclass
class FileReferenceRule:
    """ファイル参照ルールのデータクラス"""
    name: str
    phase: int
    priority: str
    trigger: str
    target: str
    prompt: str


class FileReferenceRuleParser:
    """ファイル参照ルールのパーサー"""
    
    def __init__(self, logger: Optional[logging.Logger] = None):
        self.logger = logger or logging.getLogger(__name__)
        self.rules: List[FileReferenceRule] = []
    
    def load_rules(self, project_root: Optional[Path] = None) -> List[FileReferenceRule]:
        """
        ルールファイルを読み込む
        
        優先順位:
        1. プロジェクト固有ルール (<project>/.cognix/file_reference_rules.md)
        2. デフォルトルール (~/.cognix/rules/default_file_reference_rules.md)
        
        Args:
            project_root: プロジェクトのルートディレクトリ（Noneの場合は現在のディレクトリ）
        
        Returns:
            読み込まれたルールのリスト
        """
        rules = []
        
        # デフォルトルール - 優先順位付きで複数パスを試す
        # 優先順位1: ~/.cognix/rules/default_file_reference_rules.md
        default_rules_path = Path.home() / '.cognix' / 'rules' / 'default_file_reference_rules.md'
        
        # 優先順位2: ~/.cognix/default_file_reference_rules.md
        if not default_rules_path.exists():
            default_rules_path = Path.home() / '.cognix' / 'default_file_reference_rules.md'
        
        if default_rules_path.exists():
            try:
                default_rules = self._parse_rule_file(default_rules_path)
                rules.extend(default_rules)
                self.logger.debug(f"[Rule Loader] ✅ Loaded {len(default_rules)} default rules from {default_rules_path}")
            except Exception as e:
                self.logger.warning(f"[Rule Loader] ⚠️  Failed to load default rules: {e}")
        else:
            self.logger.debug(f"[Rule Loader] ⚠️  Default rules not found in any location")
        
        # プロジェクト固有ルール (<project>/.cognix/file_reference_rules.md)
        if project_root:
            project_rules_path = project_root / '.cognix' / 'file_reference_rules.md'
            if project_rules_path.exists():
                try:
                    project_rules = self._parse_rule_file(project_rules_path)
                    
                    # 同じ名前のルールは上書き
                    default_rule_names = {r.name for r in rules}
                    for rule in project_rules:
                        if rule.name in default_rule_names:
                            rules = [r for r in rules if r.name != rule.name]
                            self.logger.debug(f"[Rule Loader] 🔄 Overriding default rule: {rule.name}")
                        rules.append(rule)
                    
                    self.logger.debug(f"[Rule Loader] ✅ Loaded {len(project_rules)} project rules from {project_rules_path}")
                except Exception as e:
                    self.logger.warning(f"[Rule Loader] ⚠️  Failed to load project rules: {e}")
        
        self.rules = rules
        self.logger.debug(f"[Rule Loader] 📋 Total rules loaded: {len(rules)}")
        return rules
    
    def _parse_rule_file(self, filepath: Path) -> List[FileReferenceRule]:
        """
        Markdownルールファイルをパース
        
        フォーマット:
        ## Rule: [Rule-Name]
        
        **Phase:** [1|2|3]
        **Priority:** [Critical|High|Medium|Low]
        **Trigger:** [Condition]
        **Target:** [Condition]
        
        **Prompt:**
        ```
        [Prompt text]
        ```
        
        Args:
            filepath: ルールファイルのパス
        
        Returns:
            パースされたルールのリスト
        """
        rules = []
        
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
        
        # "## Rule:" で始まるセクションを分割
        rule_sections = re.split(r'\n## Rule:\s*', content)
        
        for section in rule_sections[1:]:  # 最初のセクションはヘッダーなのでスキップ
            try:
                rule = self._parse_rule_section(section)
                if rule:
                    rules.append(rule)
            except Exception as e:
                self.logger.warning(f"[Rule Parser] Failed to parse rule section: {e}")
                continue
        
        return rules
    
    def _parse_rule_section(self, section: str) -> Optional[FileReferenceRule]:
        """
        ルールセクションをパース
        
        Args:
            section: ルールセクションのテキスト
        
        Returns:
            FileReferenceRuleオブジェクト、またはNone
        """
        # ルール名を抽出（最初の行）
        lines = section.split('\n')
        name = lines[0].strip()
        
        # メタデータを抽出
        phase_match = re.search(r'\*\*Phase:\*\*\s*(\d+)', section)
        priority_match = re.search(r'\*\*Priority:\*\*\s*(Critical|High|Medium|Low)', section)
        trigger_match = re.search(r'\*\*Trigger:\*\*\s*([^\n]+)', section)
        target_match = re.search(r'\*\*Target:\*\*\s*([^\n]+)', section)
        
        if not all([phase_match, priority_match, trigger_match, target_match]):
            self.logger.debug(f"[Rule Parser] Incomplete rule metadata: {name}")
            return None
        
        phase = int(phase_match.group(1))
        priority = priority_match.group(1)
        trigger = trigger_match.group(1).strip()
        target = target_match.group(1).strip()
        
        # プロンプトを抽出
        # **Prompt:** の後の ```...``` ブロックを抽出
        prompt_match = re.search(r'\*\*Prompt:\*\*\s*```\s*\n(.*?)\n```', section, re.DOTALL)
        if not prompt_match:
            self.logger.warning(f"[Rule Parser] No prompt found for rule: {name}")
            return None
        
        prompt = prompt_match.group(1).strip()
        
        return FileReferenceRule(
            name=name,
            phase=phase,
            priority=priority,
            trigger=trigger,
            target=target,
            prompt=prompt
        )
    
    def evaluate_trigger(self, rule: FileReferenceRule, filepaths: List[str]) -> bool:
        """
        トリガー条件を評価
        
        対応するパターン:
        - "HTML files exist (*.html)" → any(f.endswith('.html') for f in filepaths)
        - "React component files exist (*.jsx, *.tsx)" → 複数の拡張子
        - "Progressive Web App keywords detected (PWA, service worker, offline)" → キーワード検索
        - "Flask project detected" → .pyファイルが存在
        - "Django project detected" → .pyファイルが存在
        - "FastAPI detected" → .pyファイルが存在
        
        Args:
            rule: 評価するルール
            filepaths: ファイルパスのリスト
        
        Returns:
            条件を満たす場合True
        """
        trigger = rule.trigger.lower()
        
        # パターン1: "files exist (*.ext)" 形式
        exist_match = re.search(r'files? exist.*?\(([\*\.\w\s,]+)\)', trigger)
        if exist_match:
            extensions = exist_match.group(1)
            # 拡張子を抽出（例: "*.html, *.htm" → [".html", ".htm"]）
            ext_list = [ext.strip().replace('*', '') for ext in extensions.split(',')]
            return any(
                any(filepath.endswith(ext) for ext in ext_list)
                for filepath in filepaths
            )
        
        # パターン2: "keywords detected (...)" 形式
        keyword_match = re.search(r'keywords? detected.*?\(([^\)]+)\)', trigger)
        if keyword_match:
            keywords = [kw.strip().lower() for kw in keyword_match.group(1).split(',')]
            # ファイルパスまたはファイル名にキーワードが含まれるか確認
            return any(
                any(keyword in filepath.lower() for keyword in keywords)
                for filepath in filepaths
            )
        
        # パターン3: "directives detected in CSS" 形式（Tailwind等）
        if 'directives detected' in trigger or 'directive detected' in trigger:
            # CSSファイルが存在するか確認（実際のファイル内容のチェックは省略）
            return any(filepath.endswith('.css') for filepath in filepaths)
        
        # パターン4: フレームワーク検出パターン（Flask, Django, FastAPI等）
        # "Flask project detected", "Django project detected", "FastAPI detected" など
        framework_patterns = {
            'flask': ['.py', '.html'],  # FlaskはPythonとHTMLが必要
            'django': ['.py', '.html'],
            'fastapi': ['.py'],
            'express': ['.js'],
        }
        for framework, required_exts in framework_patterns.items():
            if f'{framework} project detected' in trigger or f'{framework} detected' in trigger:
                # 必要な拡張子のファイルが存在するか確認
                has_required = all(
                    any(filepath.endswith(ext) for filepath in filepaths)
                    for ext in required_exts
                )
                if has_required:
                    self.logger.debug(f"[Rule Evaluator] ✅ {framework.capitalize()} framework detected")
                    return True
                return False
        
        # パターン5: "Blueprints detected" など特定のキーワード
        if 'blueprint' in trigger:
            return any(filepath.endswith('.py') for filepath in filepaths)
        
        # パターン6: ファイル存在チェック（requirements.txt exists等）
        file_exists_patterns = [
            ('requirements.txt exists', 'requirements.txt'),
            ('package.json exists', 'package.json'),
            ('dockerfile exists', 'dockerfile'),
            ('next.config' in trigger, 'next.config'),
            ('nuxt.config' in trigger, 'nuxt.config'),
            ('remix.config' in trigger, 'remix.config'),
        ]
        for pattern, filename in file_exists_patterns:
            if pattern in trigger if isinstance(pattern, str) else pattern:
                return any(filename.lower() in filepath.lower() for filepath in filepaths)
        
        # パターン7: "HTML files referencing" など参照チェック
        if 'html files referencing' in trigger or 'referencing css' in trigger or 'referencing js' in trigger:
            return any(filepath.endswith('.html') for filepath in filepaths)
        
        # パターン8: "any ui-related files" → HTML/CSS/JSのいずれかが存在
        if 'ui-related' in trigger or 'ui related' in trigger:
            ui_extensions = ['.html', '.css', '.js', '.jsx', '.tsx', '.vue', '.svelte']
            return any(
                any(filepath.endswith(ext) for ext in ui_extensions)
                for filepath in filepaths
            )
        
        # パターン9: "calculator pattern detected" → 特定のパターン名
        if 'pattern detected' in trigger:
            # パターン名を抽出（例: "calculator pattern detected" → "calculator"）
            pattern_match = re.search(r'(\w+)\s+pattern\s+detected', trigger)
            if pattern_match:
                # パターン名がファイル名に含まれるか確認
                pattern_name = pattern_match.group(1)
                return any(pattern_name in filepath.lower() for filepath in filepaths)
        
        # パターン10: "package.json with scripts exists" → package.jsonが存在
        if 'package.json with' in trigger:
            return any('package.json' in filepath.lower() for filepath in filepaths)
        
        # デフォルト: 条件を満たさない
        self.logger.debug(f"[Rule Evaluator] Unknown trigger pattern: {trigger}")
        return False
    
    def evaluate_target(self, rule: FileReferenceRule, filepaths: List[str]) -> bool:
        """
        ターゲット条件を評価
        
        Args:
            rule: 評価するルール
            filepaths: ファイルパスのリスト
        
        Returns:
            条件を満たす場合True
        """
        target = rule.target.lower()
        
        # "Always" の場合は常にTrue
        if target == 'always':
            return True
        
        # それ以外はevaluate_triggerと同じロジック
        return self.evaluate_trigger(
            FileReferenceRule(
                name=rule.name,
                phase=rule.phase,
                priority=rule.priority,
                trigger=rule.target,  # targetをtriggerとして評価
                target='Always',
                prompt=rule.prompt
            ),
            filepaths
        )
    
    def get_applicable_rules(
        self, 
        filepaths: List[str], 
        max_phase: int = 3,
        priority_filter: Optional[List[str]] = None
    ) -> List[FileReferenceRule]:
        """
        適用可能なルールを取得
        
        Args:
            filepaths: ファイルパスのリスト
            max_phase: 最大フェーズ（1-3、デフォルト: 3 = すべて）
            priority_filter: 優先順位フィルター（例: ['High', 'Medium']）
        
        Returns:
            適用可能なルールのリスト（優先順位順）
        """
        applicable = []
        
        for rule in self.rules:
            # フェーズフィルター
            if rule.phase > max_phase:
                continue
            
            # 優先順位フィルター
            if priority_filter and rule.priority not in priority_filter:
                continue
            
            # トリガーとターゲット条件を評価
            if self.evaluate_trigger(rule, filepaths) and self.evaluate_target(rule, filepaths):
                applicable.append(rule)
                self.logger.debug(f"[Rule Evaluator] ✅ Rule applicable: {rule.name} (Phase {rule.phase}, {rule.priority})")
        
        # 優先順位でソート（High → Medium → Low）
        priority_order = {'High': 0, 'Medium': 1, 'Low': 2}
        applicable.sort(key=lambda r: (r.phase, priority_order.get(r.priority, 3)))
        
        return applicable


class AppPatternDetector:
    """
    app_patterns.jsonを活用してアプリパターンを検出し、
    プロンプト強化を提供するクラス
    """
    
    def __init__(self, logger=None):
        self.logger = logger
        self.patterns = {}
        self._load_patterns()
    
    def _load_patterns(self):
        """app_patterns.jsonを読み込む"""
        try:
            # 優先順位1: ~/.cognix/app_patterns.json
            patterns_path = Path.home() / '.cognix' / 'app_patterns.json'
            
            # 優先順位2: ~/.cognix/patterns/app_patterns.json
            if not patterns_path.exists():
                patterns_path = Path.home() / '.cognix' / 'patterns' / 'app_patterns.json'
            
            if patterns_path.exists():
                with open(patterns_path, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.patterns = data.get('web_app_patterns', {})
                    if self.logger:
                        self.logger.debug(f"[AppPattern] Loaded {len(self.patterns)} patterns from {patterns_path}")
            else:
                if self.logger:
                    self.logger.debug(f"[AppPattern] No app_patterns.json found")
        except Exception as e:
            if self.logger:
                self.logger.warning(f"[AppPattern] Failed to load patterns: {e}")
    
    def detect_pattern(self, goal: str) -> Optional[str]:
        """
        goalからアプリパターンを検出
        
        Args:
            goal: ユーザーの実装目標
        
        Returns:
            検出されたパターン名、または None
        """
        if not self.patterns:
            return None
        
        goal_lower = goal.lower()
        
        # 各パターンのキーワードでマッチング
        for pattern_name, config in self.patterns.items():
            trigger_config = config.get('trigger_conditions', {})
            triggers = trigger_config.get('triggers', {})
            
            # explicit_keywordsでチェック（高信頼度）
            explicit = triggers.get('explicit_keywords', {})
            if explicit.get('enabled', False):
                for keyword in explicit.get('keywords', []):
                    if keyword.lower() in goal_lower:
                        if self.logger:
                            self.logger.debug(f"[AppPattern] Detected '{pattern_name}' via explicit keyword: {keyword}")
                        return pattern_name
            
            # implicit_keywordsでチェック（中信頼度）
            implicit = triggers.get('implicit_keywords', {})
            if implicit.get('enabled', False):
                for keyword in implicit.get('keywords', []):
                    if keyword.lower() in goal_lower:
                        # 競合キーワードをチェック
                        context_reqs = implicit.get('context_requirements', [])
                        if 'no_conflicting_keywords_like_cli_or_terminal' in context_reqs:
                            if 'cli' in goal_lower or 'terminal' in goal_lower or 'command line' in goal_lower:
                                continue
                        if self.logger:
                            self.logger.debug(f"[AppPattern] Detected '{pattern_name}' via implicit keyword: {keyword}")
                        return pattern_name
        
        return None
    
    def get_prompt_enhancement(self, pattern_name: str) -> str:
        """
        パターンに基づくプロンプト強化テキストを取得
        
        Args:
            pattern_name: パターン名
        
        Returns:
            プロンプト強化テキスト
        """
        if pattern_name not in self.patterns:
            return ""
        
        config = self.patterns[pattern_name]
        pe = config.get('prompt_enhancement', {})
        
        if not pe:
            return ""
        
        # プロンプト構築
        prompt_parts = []
        
        # ベース説明
        base = pe.get('base', '')
        if base:
            prompt_parts.append(f"⚠️ APP PATTERN DETECTED: {pattern_name.upper()}")
            prompt_parts.append(base)
        
        # ファイル要件
        files = pe.get('files', {})
        if files:
            prompt_parts.append("\nREQUIRED FILES:")
            for ftype, fconfig in files.items():
                filename = fconfig.get('filename', f'{pattern_name}.{ftype}')
                purpose = fconfig.get('purpose', '')
                requirements = fconfig.get('requirements', [])
                
                prompt_parts.append(f"\n{ftype.upper()} - {filename}:")
                prompt_parts.append(f"  Purpose: {purpose}")
                if requirements:
                    prompt_parts.append("  Requirements:")
                    for req in requirements[:5]:  # 最大5件
                        prompt_parts.append(f"    - {req}")
        
        # 品質要件
        qr = pe.get('quality_requirements', {})
        if qr:
            prompt_parts.append("\nQUALITY REQUIREMENTS:")
            for key, value in list(qr.items())[:3]:  # 最大3件
                prompt_parts.append(f"  - {key}: {value[:100]}...")
        
        return "\n".join(prompt_parts)
    
    def get_required_files(self, pattern_name: str) -> List[str]:
        """
        パターンに必要なファイルリストを取得
        
        Args:
            pattern_name: パターン名
        
        Returns:
            必要なファイルタイプのリスト ['html', 'css', 'js']
        """
        if pattern_name not in self.patterns:
            return []
        
        return self.patterns[pattern_name].get('files', [])


class SemiAutoEngine:
    """Semi-automatic code generation and modification engine"""
    
    def __init__(self, llm_manager=None, workspace_path: Optional[str] = None, 
                context=None, impact_analyzer=None, related_finder=None,
                diff_engine=None, repository_analyzer=None, config=None, **kwargs):  # ⭐ 追加
        self.llm_manager = llm_manager
        self.workspace_path = Path(workspace_path) if workspace_path else Path.cwd()
        self.context = context
        self.impact_analyzer = impact_analyzer
        self.related_finder = related_finder
        self.diff_engine = diff_engine
        self.repository_analyzer = repository_analyzer  # ⭐ 追加
        self.config = config
        self.extractor = StyleCodeExtractor()
        self.session_log = []
        self.temp_dir = None
        self.backup_dir = None
        self._complexity_assessor = ComplexityAssessor()
        # Note: PromptTemplateManager was removed as it was not being used
        # Prompts are handled directly in _get_code_generation_system_prompt()

        # PostGenerationValidator の初期化 (Layer 3)
        self.post_validator = PostGenerationValidator(
            llm_manager=self.llm_manager,
            repository_analyzer=self.repository_analyzer,
            logger=logger
        )

        # RequirementValidator の初期化
        try:
            from cognix.requirement_validator import RequirementValidator
            self.requirement_validator = RequirementValidator()
        except ImportError:
            self.requirement_validator = None
            logger.debug(StatusIndicator.warning_rich("RequirementValidator not available (optional feature)"))
        except Exception as e:
            self.requirement_validator = None
            logger.debug(StatusIndicator.warning_rich(f"RequirementValidator initialization failed: {e}"))

        # DependencyChecker の初期化
        try:
            from cognix.dependency_checker import DependencyChecker
            self.dependency_checker = DependencyChecker()
        except ImportError:
            self.dependency_checker = None
            logger.debug(StatusIndicator.warning_rich("DependencyChecker not available (optional feature)"))
        except Exception as e:
            self.dependency_checker = None
            logger.debug(StatusIndicator.warning_rich(f"DependencyChecker initialization failed: {e}"))

        # LinterIntegration の初期化
        try:
            if LinterIntegration is not None:
                self.linter_integration = LinterIntegration()
            else:
                self.linter_integration = None
        except ImportError:
            self.linter_integration = None
            logger.debug(StatusIndicator.warning_rich("LinterIntegration not available (optional feature)"))
        except Exception as e:
            self.linter_integration = None
            logger.debug(StatusIndicator.warning_rich(f"LinterIntegration initialization failed: {e}"))

        self._init_workspace()
        
        # ============================================
        # 差分スキャン用Issueキャッシュの初期化
        # ============================================
        # キャッシュ構造: Dict[str, List[CachedIssue]]
        # キー: ファイルパス、値: そのファイルのCachedIssueリスト
        self._issue_cache: Optional[Dict[str, List[CachedIssue]]] = None
        
        # 差分スキャン有効フラグ（デフォルト有効）
        self._differential_scan_enabled: bool = True
        
    def _init_workspace(self):
        """Initialize workspace and backup directories"""
        self.workspace_path.mkdir(parents=True, exist_ok=True)
        
        # Create backup directory
        self.backup_dir = self.workspace_path / ".cognix_backups"
        self.backup_dir.mkdir(exist_ok=True)
        
        # Create temp directory for staging
        self.temp_dir = self.workspace_path / ".cognix_temp"
        self.temp_dir.mkdir(exist_ok=True)

    def _assess_goal_complexity(self, goal: str) -> str:
        """
        複雑度判定メソッド
        
        Args:
            goal: 実装目標の説明
            
        Returns:
            "simple" | "medium" | "complex"
        """
        # 複雑度判定実行
        complexity, details = self._complexity_assessor.assess_complexity(goal)
        
        # デバッグ情報出力(開発時のみ)
        if self.config and self.config.get('debug_complexity', False):
            logger.debug(f"\n[Complexity Assessment Debug]")
            logger.debug(f"  Goal: {goal}")
            logger.debug(f"  Complexity: {complexity}")
            logger.debug(f"  Score: {details['score']:.2f}")
            logger.debug(f"  Simple matches: {len(details['simple_matches'])}")
            logger.debug(f"  Medium matches: {len(details['medium_matches'])}")
            logger.debug(f"  Complex matches: {len(details['complex_matches_filtered'])}")
            if details['complex_matches_original'] != details['complex_matches_filtered']:
                excluded = set(details['complex_matches_original']) - set(details['complex_matches_filtered'])
                logger.debug(f"  Excluded (false positives): {excluded}")
        
        return complexity

    def _detect_target_file(self, goal: str) -> Optional[str]:
            """
            goalから対象ファイルを推測
            
            Args:
                goal: 実装目標の説明
                
            Returns:
                対象ファイル名 or None
            """
            import re
            
            # 既存ファイルをスキャン
            js_files = list(self.workspace_path.glob('*.js'))
            html_files = list(self.workspace_path.glob('*.html'))
            css_files = list(self.workspace_path.glob('*.css'))
            py_files = list(self.workspace_path.glob('*.py'))
            
            existing_files = js_files + html_files + css_files + py_files
            
            # ファイルが1つだけなら、それを対象とする
            if len(existing_files) == 1:
                logger.debug(f"Single file detected: {existing_files[0].name}")
                return existing_files[0].name
            
            # goalから直接ファイル名を検出
            # ✅ 複数のファイル名が含まれている場合は None を返す
            mentioned_files = []
            for file in existing_files:
                if file.name.lower() in goal.lower():
                    mentioned_files.append(file.name)
                    logger.debug(f"File mentioned in goal: {file.name}")

            # 複数のファイルが明示的に指定されている場合は None を返す
            # (LLM に判断を任せる)
            if len(mentioned_files) > 1:
                logger.debug(f"Multiple files mentioned in goal: {mentioned_files}. Letting LLM decide.")
                return None

            # 単一ファイルが指定されている場合はそれを使用
            if len(mentioned_files) == 1:
                logger.debug(f"Single file mentioned: {mentioned_files[0]}")
                return mentioned_files[0]
            
            # "Add to X" や "Modify X" のパターンを検出
            file_pattern = r'(?:add|modify|update|edit|change).*?(?:to|in)\s+([a-zA-Z0-9_\-\.]+\.[a-z]+)'
            match = re.search(file_pattern, goal, re.IGNORECASE)
            if match:
                filename = match.group(1)
                # 既存ファイルに存在するか確認
                for file in existing_files:
                    if file.name == filename:
                        logger.debug(f"File pattern matched: {filename}")
                        return filename
            
            # 主要なファイルを優先(script.js > index.html > styles.css)
            # ファイルタイプ別の優先順位
            # JSファイルを最優先、次にHTML、最後にCSS
            js_priority = ['script.js', 'main.js', 'app.js', 'index.js']

            # 優先JSファイルをチェック
            for priority in js_priority:
                for file in js_files:
                    if file.name == priority:
                        logger.debug(f"Using priority JS file: {priority}")
                        return priority

            # 任意のJSファイルを選択
            if js_files:
                logger.debug(f"Defaulting to first JS file: {js_files[0].name}")
                return js_files[0].name

            # JSがなければHTMLを選択
            if html_files:
                logger.debug(f"Defaulting to first HTML file: {html_files[0].name}")
                return html_files[0].name

            # HTMLがなければCSSを選択
            if css_files:
                logger.debug(f"Defaulting to first CSS file: {css_files[0].name}")
                return css_files[0].name
            
            logger.debug("No target file detected")
            return None

    # ============================================
    # 🆕 LLM-based Project Structure Detection
    # ============================================
    
    def _determine_project_structure(self, goal: str) -> ProjectStructure:
        """
        LLMでプロジェクト構造を判定
        
        キーワード検出の限界を超え、文脈を理解した判定を行う。
        例: "Web Audio API" をバックエンドではなくブラウザ内蔵APIと認識
        
        Args:
            goal: ユーザーの実装目標
            
        Returns:
            ProjectStructure(stage, file_types, reason)
        """
        prompt = f'''# Project Structure Analysis

Analyze the requirement and determine the project structure.

## Response Format

Respond with ONLY valid JSON (no markdown, no explanation):
{{"stage": "single" or "multi", "file_types": [...], "reason": "..."}}

## Stage Definitions

### single
- Frontend-only applications (HTML/CSS/JS, React, Vue)
- Simple scripts or utilities
- No backend server process required
- Client-side storage only (localStorage, IndexedDB)

### multi
- Backend server required (Flask, Express, FastAPI, etc.)
- Server-side database (PostgreSQL, MySQL, MongoDB, etc.)
- REST API / GraphQL server
- Server-side authentication/sessions

## file_types Reference

| Type | Description |
|------|-------------|
| html | HTML files |
| css | CSS stylesheets |
| js | JavaScript files |
| jsx | React JSX files |
| ts | TypeScript files |
| tsx | React TypeScript files |
| vue | Vue.js files |
| python | Python files |
| node | Node.js backend |

## Critical Clarifications

- Web Audio API, Canvas API, Fetch API = Browser built-in (NOT backend)
- localStorage, IndexedDB = Client-side storage (NOT database)
- "Single HTML file" or "single file" = stage: single, file_types: ["html"]

## Examples

Pomodoro Timer (single HTML, uses Web Audio API):
{{"stage": "single", "file_types": ["html"], "reason": "Frontend-only. Web Audio API is browser built-in. Single file requested."}}

React Dashboard:
{{"stage": "single", "file_types": ["html", "jsx", "css"], "reason": "Frontend-only React app."}}

Flask REST API:
{{"stage": "multi", "file_types": ["python"], "reason": "Backend server with Flask."}}

Flask + React Fullstack:
{{"stage": "multi", "file_types": ["python", "html", "jsx", "css"], "reason": "Fullstack with Flask backend and React frontend."}}

## Requirement

{goal}
'''
        
        try:
            logger.debug("[Project Structure] Starting LLM-based detection...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a project structure analyzer. Respond with ONLY valid JSON, no markdown code blocks.",
                max_tokens=300
            )
            
            # レスポンスからテキストを抽出
            if hasattr(response, 'content'):
                response_text = response.content
            else:
                response_text = str(response)
            
            logger.debug(f"[Project Structure] LLM response: {response_text[:200]}...")
            
            # JSON抽出（マークダウンコードブロック除去）
            cleaned = re.sub(r'```json\s*|\s*```', '', response_text.strip())
            # 先頭/末尾の空白や改行を除去
            cleaned = cleaned.strip()
            
            result = json.loads(cleaned)
            
            logger.debug(f"[Project Structure] Parsed result: {result}")
            
            # 結果を構築
            structure = ProjectStructure(
                stage=result.get('stage', 'single'),
                file_types=result.get('file_types', ['html']),
                reason=result.get('reason', '')
            )
            
            logger.debug(f"[Project Structure] ✅ LLM detection successful: stage={structure.stage}, file_types={structure.file_types}")
            
            return structure
            
        except json.JSONDecodeError as e:
            logger.warning(f"[Project Structure] JSON parse error: {e}")
            logger.warning(f"[Project Structure] Raw response: {response_text if 'response_text' in locals() else 'N/A'}")
            logger.warning("[Project Structure] Falling back to keyword detection")
            return self._fallback_project_structure(goal)
            
        except Exception as e:
            logger.warning(f"[Project Structure] LLM detection failed: {e}")
            logger.warning("[Project Structure] Falling back to keyword detection")
            return self._fallback_project_structure(goal)
    
    def _fallback_project_structure(self, goal: str) -> ProjectStructure:
        """
        フォールバック: 既存のキーワード検出からProjectStructureを生成
        
        LLM判定が失敗した場合に使用
        
        Args:
            goal: ユーザーの実装目標
            
        Returns:
            ProjectStructure(stage, file_types, reason)
        """
        logger.debug("[Project Structure] Using fallback keyword detection...")
        
        detection = self._detect_multi_file_requirement(goal)
        
        file_types = detection.get('file_types', [])
        
        # fullstack判定（既存ロジック）
        file_types_set = set(file_types)
        backend_types = {'python', 'node'}
        frontend_types = {'html', 'jsx', 'vue', 'css', 'js', 'ts', 'tsx'}
        has_backend = bool(backend_types & file_types_set)
        has_frontend = bool(frontend_types & file_types_set)
        is_fullstack = has_backend and has_frontend
        
        stage = "multi" if is_fullstack else "single"
        
        structure = ProjectStructure(
            stage=stage,
            file_types=file_types,
            reason=f"Fallback: {detection.get('reason', 'keyword detection')}"
        )
        
        logger.debug(f"[Project Structure] Fallback result: stage={structure.stage}, file_types={structure.file_types}")
        
        return structure

    def execute_semi_auto_implementation(self, goal: str) -> SemiAutoResult:
        """
        Execute semi-automated implementation based on goal
        
        Args:
            goal: Implementation goal description
            
        Returns:
            SemiAutoResult with analysis, code, and recommendations
        """
        hud = None  # HUD初期化（exceptブロックで使用）
        
        # ⭐ Zen HUD用サマリー初期化（インスタンス変数として保持）
        self._zen_summary = {
            "lint": {"initial": 0, "final": 0, "fixed": False},
            "review": {"initial": 0, "final": 0, "fixed": False, "issues": []}
        }
        
        try:
            # ==========================================
            # Phase 0: Repository分析（HUD開始前に実行）
            # ==========================================
            if self.repository_analyzer:
                repo_data_count = len(self.repository_analyzer.memory.repository_data)
                
                # 既存ファイルの事前チェック
                from pathlib import Path
                project_root = Path.cwd()
                has_files = any(
                    file.is_file() and file.suffix in ['.py', '.js', '.html', '.css', '.ts', '.jsx', '.tsx', '.json', '.md']
                    for file in project_root.glob('*')
                )
                
                # 🔧 修正: 常に再分析（ファイル数・内容変更を確実に検出）
                # 旧ロジック: キャッシュ使用（repo_data_count > 0の場合）
                # 新ロジック: 毎回分析実行（ファイル増加・関数追加を検出）
                if has_files:
                    logger.debug(f"[Repo] Starting analysis (previous cache: {repo_data_count} files)")
                    logger.debug(f"{Icon.GEAR.value} Analyzing project structure...")
                    try:
                        for progress_update in self.repository_analyzer.analyze_project_incrementally(max_files=100):
                            if progress_update.get('status'):
                                status = progress_update['status']
                                # 最後の進捗のみ表示（冗長を避ける）
                                if progress_update.get('progress', 0) == 1.0 or \
                                    progress_update.get('current_file') is None:
                                    logger.debug(f"   {status}")
                        
                        # 🔍 デバッグ: 分析完了後のファイル数を確認
                        new_repo_data_count = len(self.repository_analyzer.memory.repository_data)
                        logger.debug(f"[Repo] Analysis completed: {new_repo_data_count} files (was {repo_data_count})")
                    except Exception as analysis_error:
                        logger.debug(f"⚠  Repository analysis failed: {analysis_error}")
                        logger.warning(f"Repository analysis failed: {analysis_error}")
                else:
                    logger.debug("[Repo] No files to analyze")
            
            # Phase 1.1: Complexity評価を最初に実行（多段階生成判定のため）
            complexity = self._assess_goal_complexity(goal)
            
            # Phase 1.2: Goal制約抽出（🆕 Phase 1実装）
            goal_constraints = None
            if self.requirement_validator:
                try:
                    goal_constraints = self.requirement_validator.extract_goal_constraints(goal)
                    if goal_constraints.get("constraint_strength") in ["strict", "moderate"]:
                        logger.debug(f"[Goal Constraints] Detected: strength={goal_constraints.get('constraint_strength')}")
                        logger.debug(f"[Goal Constraints]   keep_keywords={goal_constraints.get('keep_keywords')}")
                        logger.debug(f"[Goal Constraints]   only_keywords={goal_constraints.get('only_keywords')}")
                        logger.debug(f"[Goal Constraints]   forbidden_actions={goal_constraints.get('forbidden_actions')}")
                except Exception as e:
                    logger.debug(f"[Goal Constraints] Extraction failed: {e}")
                    goal_constraints = None
            
            # インスタンス変数として保存（_generate_code_implementationで使用）
            self._current_goal_constraints = goal_constraints
            
            # 🆕 Phase 1.3: フレームワーク制約検出
            self._no_framework_mode = False
            self._required_framework = None
            goal_lower = goal.lower()
            
            # フレームワーク禁止キーワード
            no_framework_keywords = [
                'pure html', 'vanilla javascript', 'vanilla js', 
                'no framework', 'no react', 'no vue', 'plain javascript',
                'html + css + js', 'html, css, js', 'html css js',
                'html/css/js', 'without framework', 'without react',
                'html + css + javascript', 'single html', 'no frameworks'
            ]
            for keyword in no_framework_keywords:
                if keyword in goal_lower:
                    self._no_framework_mode = True
                    logger.debug(f"[Framework Restriction] No-framework mode ENABLED (keyword: '{keyword}')")
                    # 🆕 PostGenerationValidatorにも伝播
                    if hasattr(self, 'post_validator') and self.post_validator:
                        self.post_validator.no_framework_mode = True
                    break
            
            # フレームワーク指定キーワード（優先度低：禁止キーワードがない場合のみ）
            if not self._no_framework_mode:
                if 'react' in goal_lower and 'no react' not in goal_lower:
                    self._required_framework = 'react'
                elif 'vue' in goal_lower and 'no vue' not in goal_lower:
                    self._required_framework = 'vue'
                elif 'angular' in goal_lower:
                    self._required_framework = 'angular'
                
                if self._required_framework:
                    logger.debug(f"[Framework Restriction] Required framework: {self._required_framework}")
            
            # Phase 1.1: 🆕 LLMでプロジェクト構造を判定（1回のみ）
            self._project_structure = self._determine_project_structure(goal)
            logger.debug(f"[Project Structure] stage={self._project_structure.stage}, "
                         f"file_types={self._project_structure.file_types}, "
                         f"reason={self._project_structure.reason}")
            
            # 🆕 フレームワーク禁止モード時のfile_typesフィルタリング
            if self._no_framework_mode:
                # 明示的にReact/Vueも要求されている場合はハイブリッド構成と判断
                explicit_framework_request = (
                    ('react' in goal_lower and ('and react' in goal_lower or 'with react' in goal_lower or 'react for' in goal_lower)) or
                    ('vue' in goal_lower and ('and vue' in goal_lower or 'with vue' in goal_lower or 'vue for' in goal_lower))
                )
                
                if not explicit_framework_request:
                    original_types = self._project_structure.file_types
                    framework_types = ('jsx', 'tsx', 'vue')
                    filtered_types = [ft for ft in original_types if ft not in framework_types]
                    
                    if filtered_types != original_types:
                        removed = [ft for ft in original_types if ft in framework_types]
                        logger.debug(f"[Framework Restriction] Removed framework file types: {removed}")
                        logger.debug(f"[Framework Restriction] file_types: {original_types} -> {filtered_types}")
                        # ProjectStructureを更新
                        self._project_structure = ProjectStructure(
                            stage=self._project_structure.stage,
                            file_types=filtered_types,
                            reason=self._project_structure.reason
                        )
                else:
                    logger.debug(f"[Framework Restriction] Hybrid mode detected - keeping framework file types")
            
            # 🆕 stage判定（LLM判定結果を使用）
            needs_multi_stage = (self._project_structure.stage == "multi")
            
            if needs_multi_stage:
                logger.debug(f"[Multi-Stage] LLM determined multi-stage generation: {self._project_structure.reason}")
            
            if needs_multi_stage:
                # ==========================================
                # 多段階生成フロー（StepProgressなし）
                # ==========================================
                logger.normal(f"{Icon.GEAR.value} Using multi-stage generation approach...")
                
                # 多段階生成を実行
                result = self._execute_multi_stage_generation(goal, complexity)
                
                if not result or not result[0]:
                    return SemiAutoResult(
                        success=False,
                        error="Failed to generate code with multi-stage approach"
                    )
                
                
                # 返り値をアンパック
                generated_code, lint_result, quality_scores = result
                
                # analysisとplanは多段階生成では使用されないため空文字列
                analysis = "Multi-stage generation (analysis performed per phase)"
                plan = "Multi-stage generation (plan performed per phase)"
                
            else:
                # ==========================================
                # 通常生成フロー（StepProgressあり）
                # ==========================================
                
                # ==========================================
                # 通常生成フロー（StepHUD使用 - Zen HUD設計準拠）
                # ==========================================
                
                # Zen HUD: 固定行ステップ表示（設計書準拠）
                steps = [
                    "Assess goal complexity",
                    "Analyze implementation goal",
                    "Create implementation plan",
                    "Generate code implementation",
                    "Auto-completion",
                    "Lint check & Auto-fix",
                    "Quality assessment & Comprehensive Review",
                ]
                hud = StepHUD(section_title="Code Generation", steps=steps)
                hud.start()
                
                # Step 0: Complexity assessment（既に完了）
                hud.complete("Assess goal complexity")
                
                # Step 1: Analyze the goal
                hud.mark("Analyze implementation goal")
                analysis = self._analyze_implementation_goal(goal, complexity)
                
                if not analysis:
                    hud.finish(success=False)
                    return SemiAutoResult(
                        success=False,
                        error="Failed to analyze implementation goal"
                    )
                
                hud.complete("Analyze implementation goal")
                
                # Step 2: Generate implementation plan
                hud.mark("Create implementation plan")
                plan = self._create_implementation_plan(goal, analysis, complexity)
                hud.complete("Create implementation plan")

                # Step 3: Generate code
                hud.mark("Generate code implementation")
                
                # 既存ファイルを検出してforce_filenameとして渡す
                # ⚠ force_filenameを指定すると1ファイルしか生成できないため、無効化
                # LLMがRepository Mapを使って必要なファイルを判断する
                target_filename = None  # self._detect_target_file(goal)
                
                generated_code = self._generate_code_implementation(
                    goal, 
                    analysis, 
                    plan, 
                    complexity,
                    force_filename=None,  # 常にNoneを渡す
                    skip_multi_stage=True  # 🆕 シングルステージからの呼び出しを明示
                ) 
                
                if not generated_code:
                    hud.finish(success=False)
                    return SemiAutoResult(
                        success=False,
                        error="Failed to generate code implementation"
                    )
                
                hud.complete("Generate code implementation")
                # Step 4.1: Auto-completion (HUD統合版)
                # ⭐ 改善6: _auto_complete_with_import_resolutionを常に呼ぶ
                # 理由: requirement_validatorはgoalベースの予測のみで、
                # HTML内のCSS/JS参照を検証しない。_validate_import_dependencies
                # （改善2: _validate_html_resourcesを含む）は
                # _auto_complete_with_import_resolution内でのみ呼ばれるため、
                # missing_filesの有無に関係なく常に呼ぶ必要がある。
                
                validation_result = {}
                if self.requirement_validator:
                    validation_result = self.requirement_validator.validate_requirements(goal, generated_code)
                
                    if validation_result.get('missing_files'):
                        missing_count = len(validation_result['missing_files'])
                        logger.debug(f"[Auto-completion] Missing {missing_count} files detected (from requirement_validator)")
                
                # HUD: Auto-completion開始
                hud.mark("Auto-completion")
                
                # ⭐ 改善6: 常に_auto_complete_with_import_resolutionを呼ぶ
                # これにより、_validate_import_dependencies（HTML/CSS検証含む）が実行される
                logger.debug(f"[Auto-completion] Starting _auto_complete_with_import_resolution...")
                generated_code = self._auto_complete_with_import_resolution(
                    goal=goal,
                    existing_files=generated_code,
                    validation_result=validation_result,
                    complexity=complexity
                )
                
                # 自動補完後の再検証
                if self.requirement_validator:
                    revalidation_result = self.requirement_validator.validate_requirements(
                        goal,
                        generated_code
                    )
                
                    logger.debug(f"[Auto-completion] After auto-completion:")
                    logger.debug(f"[Auto-completion]   Fulfillment Score: {revalidation_result['fulfillment_score']:.2f}")
                    logger.debug(f"[Auto-completion]   Total files: {len(generated_code)}")
                
                    if revalidation_result['is_complete']:
                        logger.debug(f"[Auto-completion]   All requirements satisfied")
                    else:
                        remaining_missing = revalidation_result.get('missing_files', [])
                        if remaining_missing:
                            logger.debug(f"[Auto-completion]   Still missing: {remaining_missing}")
                
                # ==========================================
                # Step 4.3: Consistency Checks (G-1〜G-31)
                # ==========================================
                # Note: Auto-completion [DONE]はG-チェック完了後に表示
                # G-チェックはAuto-completionの一部として扱い、HUDには内部処理として非表示
                
                # G-1: HTML-CSS クラス整合性
                missing_css_classes = self._validate_html_css_class_consistency(generated_code)
                if missing_css_classes:
                    logger.debug(f"[G-1] Detected {len(missing_css_classes)} missing CSS classes")
                    logger.debug(f"\n● CSS consistency check: {len(missing_css_classes)} missing class(es)")
                    generated_code = self._auto_complete_missing_css_classes(missing_css_classes, generated_code)
                
                # G-2: HTML-JS ID整合性
                missing_html_ids = self._validate_html_js_id_consistency(generated_code)
                if missing_html_ids:
                    logger.debug(f"[G-2] Detected {len(missing_html_ids)} missing HTML IDs")
                    logger.debug(f"● HTML-JS consistency check: {len(missing_html_ids)} missing ID(s)")
                    generated_code = self._auto_complete_missing_html_ids(missing_html_ids, generated_code)
                
                # G-3: Python環境-パッケージ互換性
                compatibility_issues = self._validate_python_package_compatibility(generated_code)
                if compatibility_issues:
                    logger.debug(f"[G-3] Detected {len(compatibility_issues)} compatibility issues")
                    logger.debug(f"● Package compatibility check: {len(compatibility_issues)} issue(s)")
                    for issue in compatibility_issues:
                        logger.debug(f"  - {issue['package']}=={issue['current_version']} → {issue['fix']}")
                    generated_code = self._auto_fix_package_compatibility(compatibility_issues, generated_code)
                
                # G-4: Pythonシンボルレベルimport整合性
                symbol_issues = self._validate_python_symbol_imports(generated_code)
                if symbol_issues:
                    logger.debug(f"[G-4] Detected {len(symbol_issues)} symbol import issues")
                    logger.debug(f"● Symbol import check: {len(symbol_issues)} issue(s)")
                    for issue in symbol_issues[:5]:
                        logger.debug(f"  - {issue['importer']}: '{issue['symbol']}' not in {issue['module']}")
                    generated_code = self._auto_fix_symbol_imports(symbol_issues, generated_code)
                
                # G-5: Import vs Requirements.txt 整合性チェック
                requirements_issues = self._validate_import_requirements_consistency(generated_code)
                if requirements_issues:
                    logger.debug(f"[G-5] Detected {len(requirements_issues)} missing packages in requirements.txt")
                    logger.debug(f"● Requirements.txt check: {len(requirements_issues)} missing package(s)")
                    for pkg in requirements_issues[:5]:
                        logger.debug(f"  - {pkg['import_name']} → {pkg['package_name']}")
                    generated_code = self._auto_fix_requirements_txt(requirements_issues, generated_code)
                
                # G-6: ライブラリ要求パターンチェック
                library_issues = self._validate_library_requirements(generated_code)
                if library_issues:
                    logger.debug(f"[G-6] Detected {len(library_issues)} library requirement issues")
                    logger.debug(f"● Library requirements check: {len(library_issues)} issue(s)")
                    for issue in library_issues[:5]:
                        logger.debug(f"  - [{issue['library']}] {issue['requirement']}")
                    generated_code = self._auto_fix_library_requirements(library_issues, generated_code)
                
                # G-7: フロントエンド依存整合性チェック
                frontend_issues = self._validate_frontend_dependencies(generated_code)
                if frontend_issues:
                    logger.debug(f"[G-7] Detected {len(frontend_issues)} frontend dependency issues")
                    logger.debug(f"● Frontend dependencies check: {len(frontend_issues)} issue(s)")
                    for issue in frontend_issues[:5]:
                        logger.debug(f"  - [{issue['type']}] {issue['dependency']}")
                    generated_code = self._auto_fix_frontend_dependencies(frontend_issues, generated_code)
                
                # G-8: Model↔Schema整合性チェック
                schema_issues = self._validate_model_schema_consistency(generated_code)
                if schema_issues:
                    logger.debug(f"[G-8] Detected {len(schema_issues)} model-schema consistency issues")
                    logger.debug(f"● Model-Schema consistency check: {len(schema_issues)} issue(s)")
                    for issue in schema_issues[:5]:
                        logger.debug(f"  - {issue['schema']}.{issue['field']} not in {issue['model']}")
                    generated_code = self._auto_fix_model_schema_consistency(schema_issues, generated_code)
                
                # G-9: ドキュメント環境依存チェック
                doc_issues = self._validate_document_environment_commands(generated_code)
                if doc_issues:
                    logger.debug(f"[G-9] Detected {len(doc_issues)} environment-dependent commands")
                    logger.debug(f"● Document environment check: {len(doc_issues)} issue(s)")
                    for issue in doc_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                    generated_code = self._auto_fix_document_environment_commands(doc_issues, generated_code)
                
                # G-10: クラス定義安全性チェック（再検証ループ付き）
                MAX_G10_FIX_ATTEMPTS = 2
                class_safety_issues = []
                for g10_attempt in range(MAX_G10_FIX_ATTEMPTS):
                    class_safety_issues = self._validate_class_definition_safety(generated_code)
                    if not class_safety_issues:
                        if g10_attempt > 0:
                            logger.debug(f"[G-10] ✅ All issues fixed after {g10_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-10] Detected {len(class_safety_issues)} issues (attempt {g10_attempt + 1}/{MAX_G10_FIX_ATTEMPTS})")
                    if g10_attempt == 0:
                        logger.debug(f"● Class definition safety check: {len(class_safety_issues)} issue(s)")
                        for issue in class_safety_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                    
                    generated_code = self._auto_fix_class_definition_safety(class_safety_issues, generated_code)
                
                if class_safety_issues:
                    logger.warning(f"[G-10] ⚠ {len(class_safety_issues)} issues remain after {MAX_G10_FIX_ATTEMPTS} fix attempts")
                
                # G-11: フレームワーク予約語チェック（再検証ループ付き）
                MAX_G11_FIX_ATTEMPTS = 2
                framework_reserved_issues = []
                for g11_attempt in range(MAX_G11_FIX_ATTEMPTS):
                    framework_reserved_issues = self._validate_framework_reserved_words(generated_code)
                    if not framework_reserved_issues:
                        if g11_attempt > 0:
                            logger.debug(f"[G-11] ✅ All issues fixed after {g11_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-11] Detected {len(framework_reserved_issues)} issues (attempt {g11_attempt + 1}/{MAX_G11_FIX_ATTEMPTS})")
                    if g11_attempt == 0:
                        logger.debug(f"● Framework reserved word check: {len(framework_reserved_issues)} issue(s)")
                        for issue in framework_reserved_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['reserved_word']}")
                    
                    generated_code = self._auto_fix_framework_reserved_words(framework_reserved_issues, generated_code)
                
                if framework_reserved_issues:
                    logger.warning(f"[G-11] ⚠ {len(framework_reserved_issues)} issues remain after {MAX_G11_FIX_ATTEMPTS} fix attempts")
                
                # G-12: Python組み込み名シャドウイングチェック（再検証ループ付き）
                MAX_G12_FIX_ATTEMPTS = 2
                builtin_shadowing_issues = []
                for g12_attempt in range(MAX_G12_FIX_ATTEMPTS):
                    builtin_shadowing_issues = self._validate_python_builtin_shadowing(generated_code)
                    if not builtin_shadowing_issues:
                        if g12_attempt > 0:
                            logger.debug(f"[G-12] ✅ All issues fixed after {g12_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-12] Detected {len(builtin_shadowing_issues)} issues (attempt {g12_attempt + 1}/{MAX_G12_FIX_ATTEMPTS})")
                    if g12_attempt == 0:
                        logger.debug(f"● Python builtin shadowing check: {len(builtin_shadowing_issues)} issue(s)")
                        for issue in builtin_shadowing_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['builtin_name']}")
                    
                    generated_code = self._auto_fix_python_builtin_shadowing(builtin_shadowing_issues, generated_code)
                
                if builtin_shadowing_issues:
                    logger.warning(f"[G-12] ⚠ {len(builtin_shadowing_issues)} issues remain after {MAX_G12_FIX_ATTEMPTS} fix attempts")
                
                # G-13: Flask Blueprintルート重複チェック（再検証ループ付き）
                MAX_G13_FIX_ATTEMPTS = 2
                route_duplicate_issues = []
                for g13_attempt in range(MAX_G13_FIX_ATTEMPTS):
                    route_duplicate_issues = self._validate_flask_route_duplicates(generated_code)
                    if not route_duplicate_issues:
                        if g13_attempt > 0:
                            logger.debug(f"[G-13] ✅ All issues fixed after {g13_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-13] Detected {len(route_duplicate_issues)} issues (attempt {g13_attempt + 1}/{MAX_G13_FIX_ATTEMPTS})")
                    if g13_attempt == 0:
                        logger.debug(f"● Flask route duplicate check: {len(route_duplicate_issues)} issue(s)")
                        for issue in route_duplicate_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['method']} {issue['route']}")
                    
                    generated_code = self._auto_fix_flask_route_duplicates(route_duplicate_issues, generated_code)
                
                if route_duplicate_issues:
                    logger.warning(f"[G-13] ⚠ {len(route_duplicate_issues)} issues remain after {MAX_G13_FIX_ATTEMPTS} fix attempts")
                
                # G-14: SQLAlchemyテーブル名重複チェック（再検証ループ付き）
                MAX_G14_FIX_ATTEMPTS = 2
                table_duplicate_issues = []
                for g14_attempt in range(MAX_G14_FIX_ATTEMPTS):
                    table_duplicate_issues = self._validate_sqlalchemy_table_duplicates(generated_code)
                    if not table_duplicate_issues:
                        if g14_attempt > 0:
                            logger.debug(f"[G-14] ✅ All issues fixed after {g14_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-14] Detected {len(table_duplicate_issues)} issues (attempt {g14_attempt + 1}/{MAX_G14_FIX_ATTEMPTS})")
                    if g14_attempt == 0:
                        logger.debug(f"● SQLAlchemy table duplicate check: {len(table_duplicate_issues)} issue(s)")
                        for issue in table_duplicate_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - table '{issue['table']}'")
                    
                    generated_code = self._auto_fix_sqlalchemy_table_duplicates(table_duplicate_issues, generated_code)
                
                if table_duplicate_issues:
                    logger.warning(f"[G-14] ⚠ {len(table_duplicate_issues)} issues remain after {MAX_G14_FIX_ATTEMPTS} fix attempts")
                
                # G-15: JavaScript予約語チェック（再検証ループ付き）
                MAX_G15_FIX_ATTEMPTS = 2
                js_reserved_issues = []
                for g15_attempt in range(MAX_G15_FIX_ATTEMPTS):
                    js_reserved_issues = self._validate_javascript_reserved_words(generated_code)
                    if not js_reserved_issues:
                        if g15_attempt > 0:
                            logger.debug(f"[G-15] ✅ All issues fixed after {g15_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-15] Detected {len(js_reserved_issues)} issues (attempt {g15_attempt + 1}/{MAX_G15_FIX_ATTEMPTS})")
                    if g15_attempt == 0:
                        logger.debug(f"● JavaScript reserved word check: {len(js_reserved_issues)} issue(s)")
                        for issue in js_reserved_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['reserved_word']}")
                    
                    generated_code = self._auto_fix_javascript_reserved_words(js_reserved_issues, generated_code)
                
                if js_reserved_issues:
                    logger.warning(f"[G-15] ⚠ {len(js_reserved_issues)} issues remain after {MAX_G15_FIX_ATTEMPTS} fix attempts")
                
                # G-16: SQLAlchemy relationship参照先検証（再検証ループ付き）
                MAX_G16_FIX_ATTEMPTS = 2
                relationship_issues = []
                for g16_attempt in range(MAX_G16_FIX_ATTEMPTS):
                    relationship_issues = self._validate_relationship_references(generated_code)
                    if not relationship_issues:
                        if g16_attempt > 0:
                            logger.debug(f"[G-16] ✅ All issues fixed after {g16_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-16] Detected {len(relationship_issues)} issues (attempt {g16_attempt + 1}/{MAX_G16_FIX_ATTEMPTS})")
                    if g16_attempt == 0:
                        logger.debug(f"● Relationship reference check: {len(relationship_issues)} issue(s)")
                        for issue in relationship_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                    
                    generated_code = self._auto_fix_relationship_references(relationship_issues, generated_code)
                
                if relationship_issues:
                    logger.warning(f"[G-16] ⚠ {len(relationship_issues)} issues remain after {MAX_G16_FIX_ATTEMPTS} fix attempts")
                
                # G-17: ForeignKey参照先検証（再検証ループ付き）
                MAX_G17_FIX_ATTEMPTS = 2
                foreignkey_issues = []
                for g17_attempt in range(MAX_G17_FIX_ATTEMPTS):
                    foreignkey_issues = self._validate_foreignkey_references(generated_code)
                    if not foreignkey_issues:
                        if g17_attempt > 0:
                            logger.debug(f"[G-17] ✅ All issues fixed after {g17_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-17] Detected {len(foreignkey_issues)} issues (attempt {g17_attempt + 1}/{MAX_G17_FIX_ATTEMPTS})")
                    if g17_attempt == 0:
                        logger.debug(f"● ForeignKey reference check: {len(foreignkey_issues)} issue(s)")
                        for issue in foreignkey_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['referenced_table']}")
                    
                    generated_code = self._auto_fix_foreignkey_references(foreignkey_issues, generated_code)
                
                if foreignkey_issues:
                    logger.warning(f"[G-17] ⚠ {len(foreignkey_issues)} issues remain after {MAX_G17_FIX_ATTEMPTS} fix attempts")
                
                # G-18: SQLAlchemy Enum定義検証（再検証ループ付き）
                MAX_G18_FIX_ATTEMPTS = 2
                enum_usage_issues = []
                for g18_attempt in range(MAX_G18_FIX_ATTEMPTS):
                    enum_usage_issues = self._validate_sqlalchemy_enum_usage(generated_code)
                    if not enum_usage_issues:
                        if g18_attempt > 0:
                            logger.debug(f"[G-18] ✅ All issues fixed after {g18_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-18] Detected {len(enum_usage_issues)} issues (attempt {g18_attempt + 1}/{MAX_G18_FIX_ATTEMPTS})")
                    if g18_attempt == 0:
                        logger.debug(f"● Enum usage check: {len(enum_usage_issues)} issue(s)")
                        for issue in enum_usage_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['referenced_class']}")
                    
                    generated_code = self._auto_fix_sqlalchemy_enum_usage(enum_usage_issues, generated_code)
                
                if enum_usage_issues:
                    logger.warning(f"[G-18] ⚠ {len(enum_usage_issues)} issues remain after {MAX_G18_FIX_ATTEMPTS} fix attempts")
                
                # G-19: Mapped型アノテーション整合性検証（再検証ループ付き）
                MAX_G19_FIX_ATTEMPTS = 2
                mapped_type_issues = []
                for g19_attempt in range(MAX_G19_FIX_ATTEMPTS):
                    mapped_type_issues = self._validate_mapped_type_consistency(generated_code)
                    if not mapped_type_issues:
                        if g19_attempt > 0:
                            logger.debug(f"[G-19] ✅ All issues fixed after {g19_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-19] Detected {len(mapped_type_issues)} issues (attempt {g19_attempt + 1}/{MAX_G19_FIX_ATTEMPTS})")
                    if g19_attempt == 0:
                        logger.debug(f"● Mapped type consistency check: {len(mapped_type_issues)} issue(s)")
                        for issue in mapped_type_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['attribute']}")
                    
                    generated_code = self._auto_fix_mapped_type_consistency(mapped_type_issues, generated_code)
                
                if mapped_type_issues:
                    logger.warning(f"[G-19] ⚠ {len(mapped_type_issues)} issues remain after {MAX_G19_FIX_ATTEMPTS} fix attempts")
                
                # G-20: Flask静的パス検証（再検証ループ付き）
                MAX_G20_FIX_ATTEMPTS = 2
                flask_static_issues = []
                for g20_attempt in range(MAX_G20_FIX_ATTEMPTS):
                    flask_static_issues = self._validate_flask_static_paths(generated_code)
                    if not flask_static_issues:
                        if g20_attempt > 0:
                            logger.debug(f"[G-20] ✅ All issues fixed after {g20_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-20] Detected {len(flask_static_issues)} issues (attempt {g20_attempt + 1}/{MAX_G20_FIX_ATTEMPTS})")
                    if g20_attempt == 0:
                        logger.debug(f"● Flask static path check: {len(flask_static_issues)} issue(s)")
                        for issue in flask_static_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['current_path']} → {issue['suggested_path']}")
                    
                    generated_code = self._auto_fix_flask_static_paths(flask_static_issues, generated_code)
                
                if flask_static_issues:
                    logger.warning(f"[G-20] ⚠ {len(flask_static_issues)} issues remain after {MAX_G20_FIX_ATTEMPTS} fix attempts")
                
                # G-21: HTML参照整合性検証（再検証ループ付き）
                MAX_G21_FIX_ATTEMPTS = 2
                html_asset_issues = []
                for g21_attempt in range(MAX_G21_FIX_ATTEMPTS):
                    html_asset_issues = self._validate_html_asset_references(generated_code)
                    if not html_asset_issues:
                        if g21_attempt > 0:
                            logger.debug(f"[G-21] ✅ All issues fixed after {g21_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-21] Detected {len(html_asset_issues)} issues (attempt {g21_attempt + 1}/{MAX_G21_FIX_ATTEMPTS})")
                    if g21_attempt == 0:
                        logger.debug(f"● HTML asset reference check: {len(html_asset_issues)} issue(s)")
                        for issue in html_asset_issues[:5]:
                            logger.debug(f"  - {issue['html_file']}:{issue['line']} - {issue['ref_type']}: {issue['ref_path']}")
                    
                    generated_code = self._auto_fix_html_asset_references(html_asset_issues, generated_code)
                
                if html_asset_issues:
                    logger.warning(f"[G-21] ⚠ {len(html_asset_issues)} issues remain after {MAX_G21_FIX_ATTEMPTS} fix attempts")
                
                # G-22: JavaScriptアニメーションループパターン検証（再検証ループ付き）
                MAX_G22_FIX_ATTEMPTS = 2
                js_animation_issues = []
                for g22_attempt in range(MAX_G22_FIX_ATTEMPTS):
                    js_animation_issues = self._validate_js_animation_loop(generated_code)
                    if not js_animation_issues:
                        if g22_attempt > 0:
                            logger.debug(f"[G-22] ✅ All issues fixed after {g22_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-22] Detected {len(js_animation_issues)} issues (attempt {g22_attempt + 1}/{MAX_G22_FIX_ATTEMPTS})")
                    if g22_attempt == 0:
                        logger.debug(f"● JavaScript animation loop check: {len(js_animation_issues)} issue(s)")
                        for issue in js_animation_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['method_name']}() called without timestamp")
                    
                    generated_code = self._auto_fix_js_animation_loop(js_animation_issues, generated_code)
                
                if js_animation_issues:
                    logger.warning(f"[G-22] ⚠ {len(js_animation_issues)} issues remain after {MAX_G22_FIX_ATTEMPTS} fix attempts")
                
                # G-23: API契約整合性チェック（警告のみ、自動修正なし）
                api_contract_issues = self._validate_api_contract_consistency(generated_code)
                if api_contract_issues:
                    logger.debug(f"● API contract consistency: {len(api_contract_issues)} warning(s)")
                    for issue in api_contract_issues[:3]:
                        logger.debug(f"  - {issue['details']}")
                    # 警告のみなので、自動修正は呼び出さない
                
                # G-24: back_populates相互参照チェック
                MAX_G24_FIX_ATTEMPTS = 2
                back_populates_issues = []
                for g24_attempt in range(MAX_G24_FIX_ATTEMPTS):
                    back_populates_issues = self._validate_back_populates_consistency(generated_code)
                    if not back_populates_issues:
                        if g24_attempt > 0:
                            logger.debug(f"[G-24] ✅ All issues fixed after {g24_attempt} attempt(s)")
                        break
                    logger.debug(f"[G-24] Detected {len(back_populates_issues)} issues (attempt {g24_attempt + 1}/{MAX_G24_FIX_ATTEMPTS})")
                    generated_code = self._auto_fix_back_populates_consistency(back_populates_issues, generated_code)
                
                if back_populates_issues:
                    logger.debug(f"● back_populates consistency: {len(back_populates_issues)} error(s)")
                    for issue in back_populates_issues[:3]:
                        logger.debug(f"  - {issue['details']}")
                
                # G-25: ForeignKey型注釈欠如チェック
                foreignkey_annotation_issues = self._validate_foreignkey_type_annotation(generated_code)
                if foreignkey_annotation_issues:
                    logger.debug(f"[G-25] Detected {len(foreignkey_annotation_issues)} issues, auto-fixing...")
                    generated_code = self._auto_fix_foreignkey_type_annotation(foreignkey_annotation_issues, generated_code)
                    foreignkey_annotation_issues = self._validate_foreignkey_type_annotation(generated_code)
                
                if foreignkey_annotation_issues:
                    logger.debug(f"● ForeignKey type annotations: {len(foreignkey_annotation_issues)} warning(s)")
                
                # G-26: Mapped型とSQLAlchemy型の不一致チェック
                mapped_mismatch_issues = self._validate_mapped_sqlalchemy_type_mismatch(generated_code)
                if mapped_mismatch_issues:
                    logger.debug(f"[G-26] Detected {len(mapped_mismatch_issues)} issues, auto-fixing...")
                    generated_code = self._auto_fix_mapped_sqlalchemy_type_mismatch(mapped_mismatch_issues, generated_code)
                    mapped_mismatch_issues = self._validate_mapped_sqlalchemy_type_mismatch(generated_code)
                
                if mapped_mismatch_issues:
                    logger.debug(f"● Mapped/SQLAlchemy type mismatch: {len(mapped_mismatch_issues)} warning(s)")
                
                # G-27: relationship overlaps検出
                relationship_overlap_issues = self._validate_relationship_overlaps(generated_code)
                if relationship_overlap_issues:
                    generated_code = self._auto_fix_relationship_overlaps(relationship_overlap_issues, generated_code)
                    logger.debug(f"● Relationship overlaps: {len(relationship_overlap_issues)} warning(s)")
                
                # G-28: 循環インポート検出
                circular_import_issues = self._validate_circular_imports(generated_code)
                if circular_import_issues:
                    generated_code = self._auto_fix_circular_imports(circular_import_issues, generated_code)
                    logger.debug(f"● Circular imports: {len(circular_import_issues)} error(s)")
                    for issue in circular_import_issues[:2]:
                        logger.debug(f"  - {issue['details']}")
                
                # G-29: Marshmallow Nested循環参照検出
                marshmallow_circular_issues = self._validate_marshmallow_nested_circular(generated_code)
                if marshmallow_circular_issues:
                    generated_code = self._auto_fix_marshmallow_nested_circular(marshmallow_circular_issues, generated_code)
                    logger.debug(f"● Marshmallow Nested circular: {len(marshmallow_circular_issues)} warning(s)")
                
                # G-30: JavaScript this参照喪失チェック
                js_this_loss_issues = self._validate_js_this_context_loss(generated_code)
                if js_this_loss_issues:
                    logger.debug(f"[G-30] Detected {len(js_this_loss_issues)} issues, auto-fixing...")
                    generated_code = self._auto_fix_js_this_context_loss(js_this_loss_issues, generated_code)
                    js_this_loss_issues = self._validate_js_this_context_loss(generated_code)
                
                if js_this_loss_issues:
                    logger.debug(f"● JavaScript this context loss: {len(js_this_loss_issues)} error(s)")
                    for issue in js_this_loss_issues[:3]:
                        logger.debug(f"  - {issue['details']}")
                
                # G-31: Marshmallow API誤用検出（再検証ループ付き）
                MAX_G31_FIX_ATTEMPTS = 2
                marshmallow_api_issues = []
                for g31_attempt in range(MAX_G31_FIX_ATTEMPTS):
                    marshmallow_api_issues = self._validate_marshmallow_api_usage(generated_code)
                    if not marshmallow_api_issues:
                        if g31_attempt > 0:
                            logger.debug(f"[G-31] ✅ All issues fixed after {g31_attempt} attempt(s)")
                        break
                    
                    logger.debug(f"[G-31] Detected {len(marshmallow_api_issues)} issues (attempt {g31_attempt + 1}/{MAX_G31_FIX_ATTEMPTS})")
                    if g31_attempt == 0:
                        logger.debug(f"● Marshmallow API usage check: {len(marshmallow_api_issues)} issue(s)")
                        for issue in marshmallow_api_issues[:5]:
                            logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['field']}")
                    
                    generated_code = self._auto_fix_marshmallow_api_usage(marshmallow_api_issues, generated_code)
                
                if marshmallow_api_issues:
                    logger.warning(f"[G-31] ⚠ {len(marshmallow_api_issues)} issues remain after {MAX_G31_FIX_ATTEMPTS} fix attempts")
                
                if not missing_css_classes and not missing_html_ids and not compatibility_issues and not symbol_issues and not requirements_issues and not library_issues and not frontend_issues and not schema_issues and not doc_issues and not class_safety_issues and not framework_reserved_issues and not builtin_shadowing_issues and not route_duplicate_issues and not table_duplicate_issues and not js_reserved_issues and not relationship_issues and not foreignkey_issues and not enum_usage_issues and not mapped_type_issues and not flask_static_issues and not html_asset_issues and not js_animation_issues and not back_populates_issues and not circular_import_issues and not js_this_loss_issues and not marshmallow_api_issues:
                    logger.debug("[G-1/.../G-31] All consistency checks passed")
                
                # HUD: Auto-completion完了（G-1〜G-31完了後）
                hud.complete("Auto-completion")

                # ==========================================
                # Step 4.5: HTML参照修正（フレームワーク禁止モード）
                # ==========================================
                if getattr(self, '_no_framework_mode', False):
                    logger.debug("[G-32] Checking HTML script references for vanilla JS...")
                    generated_code = self._fix_html_script_reference_for_vanilla_js(generated_code)

                # ==========================================
                # Step 4.6: Runtime Import Test（対策C）
                # ==========================================
                # 生成されたコードを実際にインポートして実行時エラーを検出
                # 静的解析では検出できないクラス定義時のエラーなどを事前に発見
                logger.debug("[Runtime Import] Starting import test...")
                
                import_success, import_error, failing_file = self._validate_runtime_import(generated_code)
                
                # ⭐ 対策A: 外部パッケージ不足の場合は情報表示のみ（LLM修正は不要）
                if import_success and import_error and import_error.startswith("EXTERNAL_PACKAGE:"):
                    package_name = import_error.replace("EXTERNAL_PACKAGE:", "")
                    logger.debug(f"\n● Runtime import test skipped:")
                    logger.debug(f"   ℹ Requires external package: {package_name}")
                    logger.debug(f"   → Run: pip install -r requirements.txt")
                    logger.debug(f"[Runtime Import] Skipped due to external package: {package_name}")
                elif not import_success:
                    logger.debug(f"\n⚠ Runtime import error detected:")
                    logger.debug(f"   File: {failing_file or 'Unknown'}")
                    logger.debug(f"   Error: {import_error}")
                    
                    # LLMで自動修正を試行
                    MAX_IMPORT_FIX_ATTEMPTS = 2
                    for attempt in range(MAX_IMPORT_FIX_ATTEMPTS):
                        logger.debug(f"[Runtime Import] Fix attempt {attempt + 1}/{MAX_IMPORT_FIX_ATTEMPTS}")
                        
                        fixed_code = self._fix_import_error_with_llm(
                            generated_code,
                            import_error,
                            failing_file
                        )
                        
                        if fixed_code:
                            # 修正後に再テスト
                            retry_success, retry_error, retry_file = self._validate_runtime_import(fixed_code)
                            
                            # 外部パッケージ不足になった場合もOK
                            if retry_success:
                                if retry_error and retry_error.startswith("EXTERNAL_PACKAGE:"):
                                    package_name = retry_error.replace("EXTERNAL_PACKAGE:", "")
                                    logger.debug(f"   ✅ Code error fixed. External package required: {package_name}")
                                else:
                                    logger.debug(f"   ✅ Import error fixed (attempt {attempt + 1})")
                                generated_code = fixed_code
                                logger.debug(f"[Runtime Import] ✅ Fixed on attempt {attempt + 1}")
                                break
                            else:
                                # 修正したが別のエラーが発生
                                import_error = retry_error
                                failing_file = retry_file
                                generated_code = fixed_code  # 部分的な修正を保持
                                logger.debug(f"[Runtime Import] Partial fix, new error: {retry_error}")
                        else:
                            logger.debug(f"[Runtime Import] Fix attempt {attempt + 1} failed")
                    else:
                        # 全ての試行が失敗
                        logger.debug(f"   ⚠ Could not auto-fix. Manual review required.")
                        logger.debug("[Runtime Import] All fix attempts failed")

                # Step 4: Lint check & Auto-fix (新規)
                lint_result = None  # Quality評価で使用するため初期化
                hud.mark("Lint check & Auto-fix")
                
                # Linter統合を試行 (外部linterがインストールされている場合のみ)
                try:
                    from cognix.linter_integration import LinterIntegration
                    from cognix import hud_components as hud_comp
                    
                    linter = LinterIntegration()
                    
                    # linterが利用可能な場合のみ実行
                    if linter.available_linters:
                        logger.debug("[Linter] Running external linters...")
                        lint_result = linter.lint_generated_code(generated_code)
                        
                        if lint_result['has_errors']:
                            error_count = len(lint_result['errors'])
                            warning_count = len(lint_result['warnings'])
                            linters_used = ', '.join(lint_result['linters_used'])
                            
                            logger.debug(f"[Linter] Detected {error_count} error(s), {warning_count} warning(s)")
                            logger.debug(f"[Linter] Used: {linters_used}")
                            
                            # Zen HUD: Linter要約のみ
                            error_count = len(lint_result.get('errors', []))
                            warning_count = len(lint_result.get('warnings', []))
                            ok_count = 0  # Linterはエラー/警告のみ検出
                            
                            # ⭐ Zen HUD: 初期Lintエラー数を記録
                            self._zen_summary["lint"]["initial"] = error_count
                            
                            logger.debug("")  # 空行
                            logger.debug(hud_comp.lint_summary_line(ok_count, warning_count, error_count))
                            
                            # 自動修正可能な場合はLLMに修正依頼
                            if lint_result['can_auto_fix']:
                                logger.debug("[Linter] Attempting auto-fix with LLM...")
                                logger.debug("[Linter] Auto-fix attempt started")
                                
                                fixed_files = self._fix_linter_errors_with_llm(
                                    generated_code,
                                    lint_result['errors'],
                                    lint_result.get('file_languages', {})
                                )
                                
                                if fixed_files:
                                    logger.debug("[Linter] Auto-fix successful")
                                    logger.debug(f"{Icon.GEAR.value} Linter errors have been auto-fixed")
                                    generated_code = fixed_files
                                    
                                    # ==========================================
                                    # Auto-fix後に再度Lintを実行
                                    # ==========================================
                                    try:
                                        logger.debug("[Linter] Re-running linter after auto-fix...")
                                        recheck_result = linter.lint_generated_code(generated_code)
                                        
                                        # 修正前後のエラー数を比較
                                        error_count_before = len(lint_result.get('errors', []))
                                        warning_count_before = len(lint_result.get('warnings', []))
                                        error_count_after = len(recheck_result.get('errors', []))
                                        warning_count_after = len(recheck_result.get('warnings', []))
                                        
                                        # 修正効果を表示
                                        logger.debug("")  # 空行
                                        logger.debug("  After auto-fix:")
                                        logger.debug(hud_comp.lint_summary_line(0, warning_count_after, error_count_after))
                                        
                                        # エラー内訳を表示（Option A: 簡潔版）
                                        if error_count_after > 0 or warning_count_after > 0:
                                            breakdown = self._get_error_breakdown(
                                                recheck_result.get('errors', []),
                                                recheck_result.get('warnings', [])
                                            )
                                            # 0でないカテゴリのみ表示（簡潔化）
                                            error_parts = []
                                            if breakdown['critical'] > 0:
                                                error_parts.append(f"Critical={breakdown['critical']}")
                                            if breakdown['high'] > 0:
                                                error_parts.append(f"High={breakdown['high']}")
                                            if breakdown['medium'] > 0:
                                                error_parts.append(f"Medium={breakdown['medium']}")
                                            if breakdown['low'] > 0:
                                                error_parts.append(f"Low={breakdown['low']} (style issues)")
                                            if breakdown['env'] > 0:
                                                error_parts.append(f"Env={breakdown['env']}")
                                            if breakdown['warnings'] > 0:
                                                error_parts.append(f"Warnings={breakdown['warnings']}")
                                            
                                            if error_parts:
                                                logger.debug(f"  Error Types: {', '.join(error_parts)}")

                                        
                                        # Quality評価用にlint_resultを更新（重要！）
                                        lint_result = recheck_result
                                        
                                        # ⭐ Zen HUD: Auto-fix後のLintエラー数を記録
                                        self._zen_summary["lint"]["final"] = error_count_after
                                        self._zen_summary["lint"]["fixed"] = (error_count_after < error_count_before)
                                        
                                        # 修正効果のメッセージ
                                        errors_fixed = error_count_before - error_count_after
                                        if error_count_after == 0 and warning_count_after == 0:
                                            logger.debug(f"  ✓ All linter errors resolved!")
                                            # 0でないカテゴリのみ表示（簡潔化）
                                            error_parts = []
                                            if breakdown['critical'] > 0:
                                                error_parts.append(f"Critical={breakdown['critical']}")
                                            if breakdown['high'] > 0:
                                                error_parts.append(f"High={breakdown['high']}")
                                            if breakdown['medium'] > 0:
                                                error_parts.append(f"Medium={breakdown['medium']}")
                                            if breakdown['low'] > 0:
                                                error_parts.append(f"Low={breakdown['low']} (style issues)")
                                            if breakdown['env'] > 0:
                                                error_parts.append(f"Env={breakdown['env']}")
                                            if breakdown['warnings'] > 0:
                                                error_parts.append(f"Warnings={breakdown['warnings']}")
                                            
                                            if error_parts:
                                                logger.debug(f"  Error Types: {', '.join(error_parts)}")

                                        
                                    except Exception as e:
                                        logger.debug(f"[Linter] Re-check failed: {e}")
                                        # lint_resultはAuto-fix前のままにする（fallback）

                                else:
                                    logger.debug("[Linter] Auto-fix failed")
                                    logger.debug("  ⚠  Auto-fix failed. Manual review required.")
                            else:
                                logger.debug("  → run with --verbose for details")
                                logger.debug("  ⚠  Manual review required (syntax errors detected)")
                        
                        elif lint_result['has_warnings']:
                            warning_count = len(lint_result['warnings'])
                            logger.debug(f"[Linter] Detected {warning_count} warning(s)")
                            logger.debug(f"\n● Code passed linter checks ({warning_count} warnings)")
                        else:
                            logger.debug("[Linter] No issues detected")
                            logger.debug("\n● Code passed all linter checks")
                    else:
                        logger.debug("[Linter] No external linters available (optional feature)")
                
                except ImportError:
                    logger.debug("[Linter] LinterIntegration not available (optional feature)")
                except Exception as e:
                    logger.debug(f"[Linter] Error during linting: {e}")
                
                # ==========================================
                # Cross-File Consistency Check (新規追加)
                # モデル定義と初期データの整合性を検証
                # ==========================================
                try:
                    logger.debug("[Cross-File] Starting cross-file consistency check...")
                    cross_file_result = self._check_cross_file_consistency(generated_code)
                    
                    if cross_file_result['has_issues']:
                        issue_count = len(cross_file_result['issues'])
                        logger.debug(f"[Cross-File] Detected {issue_count} consistency issue(s)")
                        
                        # 問題の詳細をログに出力
                        for issue in cross_file_result['issues']:
                            logger.debug(
                                f"[Cross-File]   {issue['model']}.{issue['field']}: "
                                f"{issue['message']}"
                            )
                        
                        # Zen HUD: 問題検出を表示
                        logger.debug("")
                        logger.debug(f"  {Icon.WARNING.value} Cross-file consistency: {issue_count} issue(s) detected")
                        
                        # LLMによる自動修正を試行
                        logger.debug("[Cross-File] Attempting auto-fix with LLM...")
                        fixed_files = self._fix_cross_file_issues_with_llm(
                            generated_code,
                            cross_file_result['issues'],
                            cross_file_result.get('model_definitions', {})
                        )
                        
                        if fixed_files:
                            # 修正されたファイルを反映
                            for filename, code in fixed_files.items():
                                generated_code[filename] = code
                            
                            logger.debug(f"[Cross-File] Auto-fix successful: {list(fixed_files.keys())}")
                            logger.debug(f"  {Icon.GEAR.value} Cross-file issues have been auto-fixed")
                            
                            # 修正後に再検証
                            recheck_result = self._check_cross_file_consistency(generated_code)
                            if recheck_result['has_issues']:
                                remaining = len(recheck_result['issues'])
                                logger.debug(f"  {Icon.WARNING.value} {remaining} issue(s) remain after auto-fix")
                            else:
                                logger.debug(f"  {Icon.CHECK.value} All cross-file issues resolved")
                        else:
                            logger.debug("[Cross-File] Auto-fix failed")
                            logger.debug(f"  {Icon.WARNING.value} Auto-fix failed. Manual review required.")
                    else:
                        logger.debug("[Cross-File] No consistency issues detected")
                
                except Exception as e:
                    logger.debug(f"[Cross-File] Error during consistency check: {e}")
                    # エラーが発生しても処理を継続
                
                # ==========================================
                # ESLint統合: JavaScript品質チェック
                # ==========================================
                eslint_result = None
                try:
                    if self._check_eslint_available():
                        logger.debug("[ESLint] Running JavaScript linter...")
                        eslint_result = self._run_eslint_on_files(generated_code)
                        
                        if eslint_result['files_checked'] > 0:
                            error_count = len(eslint_result['errors'])
                            warning_count = len(eslint_result['warnings'])
                            
                            if error_count > 0 or warning_count > 0:
                                logger.debug("")
                                logger.debug(f"● ESLint: {error_count} error(s), {warning_count} warning(s)")
                                
                                # エラー詳細を表示（最大5件）
                                for issue in eslint_result['errors'][:5]:
                                    logger.debug(f"  - {issue['file']}:{issue['line']} [{issue['rule']}] {issue['message']}")
                                
                                if error_count > 5:
                                    logger.debug(f"  ... and {error_count - 5} more error(s)")
                                
                                # LLMで自動修正
                                if error_count > 0:
                                    logger.debug("[ESLint] Attempting auto-fix with LLM...")
                                    generated_code = self._fix_eslint_errors_with_llm(
                                        generated_code,
                                        eslint_result['errors']
                                    )
                                    
                                    # 再検証
                                    recheck_result = self._run_eslint_on_files(generated_code)
                                    error_count_after = len(recheck_result['errors'])
                                    warning_count_after = len(recheck_result['warnings'])
                                    
                                    logger.debug("")
                                    logger.debug(f"  After auto-fix: {error_count_after} error(s), {warning_count_after} warning(s)")
                                    
                                    if error_count_after == 0:
                                        logger.debug(f"  ✓ All ESLint errors resolved!")
                                    
                                    eslint_result = recheck_result
                            else:
                                logger.debug("[ESLint] No issues found")
                    else:
                        logger.debug("[ESLint] Not available, skipping JavaScript lint check")
                        
                except Exception as e:
                    logger.debug(f"[ESLint] Error during lint check: {e}")
                
                hud.complete("Lint check & Auto-fix")

                # Step 4: Quality assessment & Comprehensive Review
                hud.mark("Quality assessment & Comprehensive Review")
                quality_scores = self._assess_code_quality(
                    generated_code, 
                    complexity, 
                    goal,
                    lint_result=lint_result
                )

                # Quality issues の検出と表示（初回生成時）
                quality_issues = []
                quality_issue_count = 0
                
                for filename, score in quality_scores.items():
                    if score < 0.70:  # 70%未満は問題あり
                        quality_issue_count += 1
                        # 詳細を取得
                        code = generated_code.get(filename, '')
                        ext = filename.split('.')[-1]
                        placeholder_count, detected_lines = self._detect_placeholders_in_comments(code, ext)
                        
                        if placeholder_count > 0:
                            quality_issues.append({
                                'filename': filename,
                                'score': score,
                                'issue_type': 'placeholder',
                                'count': placeholder_count,
                                'details': detected_lines[:3]  # 最大3件
                            })
                
                # Quality issues 表示（初回生成時）
                if quality_issue_count > 0:
                    logger.debug(f"\n[Quality] Code quality check: {quality_issue_count} quality issue(s) detected")
                    
                    for issue in quality_issues[:5]:  # 最大5件表示
                        logger.debug(f"  - {issue['filename']}: {issue['count']} placeholder(s) in comments")
                        for detail in issue['details']:
                            logger.debug(f"    • {detail}")
                    
                    # Quality issues の自動修正
                    if quality_issues:
                        logger.debug("[Quality] Attempting to fix quality issues...")
                        fixed_files = self._fix_quality_issues_with_llm(generated_code, quality_issues)
                        
                        if fixed_files:
                            generated_code = fixed_files
                            logger.debug("[Quality] Quality issues fixed")
                            logger.debug(f"{Icon.GEAR.value} Quality issues have been auto-fixed")
                            
                            # Quality 再評価
                            quality_scores = self._assess_code_quality(
                                generated_code,
                                complexity,
                                goal,
                                lint_result=lint_result
                            )

                # Step 4の続き: 総合レビュー（Comprehensive Review）
                # ==========================================
                # 「一発で動く」を保証するための最終チェック
                logger.debug("[Comprehensive Review] Starting final review...")
                generated_code = self._final_comprehensive_review(generated_code, goal)
                
                # 🆕 Comprehensive Review後のHTML参照修正（スタブ再生成後に必要）
                if getattr(self, '_no_framework_mode', False):
                    logger.debug("[Post-Review] Checking HTML script references after comprehensive review...")
                    generated_code = self._fix_html_script_reference_for_vanilla_js(generated_code)
                
                # 総合レビュー後のQuality再評価
                quality_scores = self._assess_code_quality(
                    generated_code,
                    complexity,
                    goal,
                    lint_result=lint_result
                )

                # Quality assessment & Comprehensive Review 完了
                hud.complete("Quality assessment & Comprehensive Review")

                # HUD終了（Auto-completion完了後）
                hud.finish(success=True)

            
            # ここから共通処理（both paths）
            

            # Step 4.7: Impact Analysis (既存コード - 変更なし)
            impact_results = {}
            if self.impact_analyzer:
                logger.verbose("Analyzing impact on existing files...")
                
                for filename, code in generated_code.items():
                    file_path = self.workspace_path / filename
                    
                    # 既存ファイルの場合のみ影響分析
                    if file_path.exists():
                        try:
                            impact = self.impact_analyzer.analyze_change_impact(
                                str(file_path),
                                change_type="modify"
                            )
                            impact_results[filename] = impact
                        except Exception as e:
                            logger.debug(f"Impact analysis error for {filename}: {e}")
            
            # Step 5: Generate recommendations (既存コード - 変更なし)
            recommendations = self._generate_recommendations(
                goal,
                analysis,
                generated_code,
                quality_scores
            )
            
            # Phase 2: Zen HUD要約表示のみ（バナー削除）
            # 設計書: 完了後は要約だけ
            
            
            # 成功結果を返す
            return SemiAutoResult(
                success=True,
                analysis=analysis,
                generated_code=generated_code,
                quality_scores=quality_scores,
                recommendations=recommendations,
                impact_analysis=impact_results,
                lint_result=lint_result,
                zen_summary=self._zen_summary  # ⭐ Zen HUD用サマリー
            )
            
        except Exception as e:
            logger.error(f"Semi-auto implementation error: {e}")
            
            # HUDが定義されている場合は失敗として終了
            try:
                if hud is not None:
                    hud.finish(success=False)
            except:
                pass  # HUD終了でエラーが発生しても無視
            
            # エラーバナーを表示（Phase 1: UI改善）
            logger.debug("")  # 空行
            print_error_banner(
                "Code generation failed",
                details=str(e)
            )
            return SemiAutoResult(
                success=False,
                error=str(e)
            )

    def retry_lint_check(self, generated_code: Dict[str, str], prev_lint_result: Optional[Dict[str, Any]] = None) -> Tuple[Dict[str, str], Optional[Dict[str, Any]], Dict[str, float]]:
        """
        Lint再実行とAuto-fix（[t] Try again用）
        
        Args:
            generated_code: 生成されたコード {filename: code}
            prev_lint_result: 前回のLint結果（改善検出用）
            
        Returns:
            Tuple[updated_code, lint_result, quality_scores]
        """
        # Note: err_console and Icon are already imported at file top (lines 13, 19)
        # Import only console and hud_comp
        try:
            from rich.console import Console
            console = Console()
        except ImportError:
            # Fallback: use err_console
            console = err_console
        
        try:
            from cognix import hud_components as hud_comp
        except ImportError:
            # Fallback: create minimal hud_comp wrapper
            class HudCompFallback:
                @staticmethod
                def lint_summary_line(ok, warn, fail):
                    from rich.text import Text
                    return Text.from_ansi(f"ⓘ Lint Summary   OK {ok} / WARN {warn} / FAIL {fail}")
            hud_comp = HudCompFallback()
        
        logger.debug("[Linter Retry] Starting Lint check & Auto-fix retry...")
        
        try:
            from cognix.linter_integration import LinterIntegration
            
            linter = LinterIntegration()
            
            # Linterが利用可能かチェック
            if not linter.available_linters:
                logger.debug("[Linter Retry] No external linters available")
                logger.debug(f"{Icon.WARNING.value} No external linters available. Cannot retry.")
                return (generated_code, prev_lint_result, {})
            
            # Lint実行
            logger.debug("[Linter Retry] Running external linters...")
            lint_result = linter.lint_generated_code(generated_code)
            
            # 前回の結果と比較（改善検出）
            if prev_lint_result:
                prev_fail_count = len(prev_lint_result.get('errors', []))
                current_fail_count = len(lint_result.get('errors', []))
            else:
                prev_fail_count = None
                current_fail_count = len(lint_result.get('errors', []))
            
            # Lint結果表示
            if lint_result['has_errors']:
                error_count = len(lint_result['errors'])
                warning_count = len(lint_result['warnings'])
                ok_count = 0
                
                logger.debug("")  # 空行
                logger.debug(hud_comp.lint_summary_line(ok_count, warning_count, error_count))
                
                # 自動修正を試行
                if lint_result['can_auto_fix']:
                    logger.debug("[Linter Retry] Attempting auto-fix with LLM...")
                    
                    fixed_files = self._fix_linter_errors_with_llm(
                        generated_code,
                        lint_result['errors'],
                        lint_result.get('file_languages', {})
                    )
                    
                    if fixed_files:
                        logger.debug("[Linter Retry] Auto-fix successful")
                        logger.debug(f"{Icon.GEAR.value} Linter errors have been auto-fixed")
                        generated_code = fixed_files
                        
                        # Auto-fix後に再度Lint実行
                        try:
                            logger.debug("[Linter Retry] Re-running linter after auto-fix...")
                            recheck_result = linter.lint_generated_code(generated_code)
                            
                            error_count_after = len(recheck_result.get('errors', []))
                            warning_count_after = len(recheck_result.get('warnings', []))
                            
                            # 修正効果を表示
                            logger.debug("")  # 空行
                            logger.debug("  After auto-fix:")
                            logger.debug(hud_comp.lint_summary_line(0, warning_count_after, error_count_after))
                            
                            # エラー内訳を表示
                            if error_count_after > 0 or warning_count_after > 0:
                                breakdown = self._get_error_breakdown(
                                    recheck_result.get('errors', []),
                                    recheck_result.get('warnings', [])
                                )
                                error_parts = []
                                if breakdown['critical'] > 0:
                                    error_parts.append(f"Critical={breakdown['critical']}")
                                if breakdown['high'] > 0:
                                    error_parts.append(f"High={breakdown['high']}")
                                if breakdown['medium'] > 0:
                                    error_parts.append(f"Medium={breakdown['medium']}")
                                if breakdown['low'] > 0:
                                    error_parts.append(f"Low={breakdown['low']} (style issues)")
                                if breakdown['env'] > 0:
                                    error_parts.append(f"Env={breakdown['env']}")
                                if breakdown['warnings'] > 0:
                                    error_parts.append(f"Warnings={breakdown['warnings']}")
                                
                                if error_parts:
                                    logger.debug(f"  Error Types: {', '.join(error_parts)}")
                            
                            # lint_resultを更新
                            lint_result = recheck_result
                            
                            # 改善検出
                            if prev_fail_count is not None:
                                current_fail_count_after = len(recheck_result.get('errors', []))
                                if current_fail_count_after < prev_fail_count:
                                    fixed_count = prev_fail_count - current_fail_count_after
                                    logger.debug("")  # 空行
                                    logger.debug(f"\033[92m{Icon.SUCCESS.value} Improved!\033[0m Fixed {fixed_count} more error(s).")
                                elif current_fail_count_after == prev_fail_count:
                                    logger.debug("")  # 空行
                                    logger.debug(f"{Icon.WARNING.value} No improvement detected. Remaining errors cannot be auto-fixed.")
                                    logger.debug("  These are minor style issues that can be safely ignored.")
                        
                        except Exception as e:
                            logger.debug(f"[Linter Retry] Re-check failed: {e}")
                    
                    else:
                        logger.debug("[Linter Retry] Auto-fix failed")
                        logger.debug("  ⚠  Auto-fix failed. Manual review required.")
                else:
                    logger.debug("  ⚠  Manual review required (syntax errors detected)")
            
            elif lint_result['has_warnings']:
                warning_count = len(lint_result['warnings'])
                logger.debug(f"[Linter Retry] Detected {warning_count} warning(s)")
                logger.debug(f"\n{Icon.INFO.value} Code passed linter checks ({warning_count} warnings)")
            else:
                logger.debug("[Linter Retry] No syntax errors detected")
                logger.debug(f"\n{Icon.SUCCESS.value} Syntax check passed (0 syntax errors, 0 warnings)")
            
            # Quality再計算
            logger.debug("[Linter Retry] Re-assessing code quality...")
            quality_scores = self._assess_code_quality(
                generated_code,
                "medium",  # 複雑度は元のまま
                "",  # goalは不要
                lint_result=lint_result
            )
            
            # Quality issues の検出と修正
            quality_issues = []
            quality_issue_count = 0
            
            for filename, score in quality_scores.items():
                if score < 0.70:  # 70%未満は問題あり
                    quality_issue_count += 1
                    # 詳細を取得
                    code = generated_code.get(filename, '')
                    ext = filename.split('.')[-1]
                    placeholder_count, detected_lines = self._detect_placeholders_in_comments(code, ext)
                    
                    if placeholder_count > 0:
                        quality_issues.append({
                            'filename': filename,
                            'score': score,
                            'issue_type': 'placeholder',
                            'count': placeholder_count,
                            'details': detected_lines[:3]  # 最大3件
                        })
            
            # Quality issues 表示と修正
            if quality_issue_count > 0:
                logger.debug(f"\n[Quality] Code quality check: {quality_issue_count} quality issue(s) detected")
                
                for issue in quality_issues[:5]:  # 最大5件表示
                    logger.debug(f"  - {issue['filename']}: {issue['count']} placeholder(s) in comments")
                    for detail in issue['details']:
                        logger.debug(f"    • {detail}")
                
                # LLM に Quality issues の修正を依頼
                if quality_issues:
                    logger.debug("[Quality] Attempting to fix quality issues with LLM...")
                    logger.debug(f"{Icon.GEAR.value} Attempting to fix quality issues...")
                    
                    fixed_files = self._fix_quality_issues_with_llm(
                        generated_code,
                        quality_issues
                    )
                    
                    if fixed_files:
                        logger.debug("[Quality] Quality fix successful")
                        logger.debug(f"{Icon.SUCCESS.value} Quality issues have been fixed")
                        generated_code = fixed_files
                        
                        # Quality 再評価
                        quality_scores = self._assess_code_quality(
                            generated_code,
                            "medium",
                            "",
                            lint_result=lint_result
                        )
                        
                        # 改善を表示
                        improved_count = 0
                        for filename, new_score in quality_scores.items():
                            old_issue = next((i for i in quality_issues if i['filename'] == filename), None)
                            if old_issue and new_score >= 0.70:
                                improved_count += 1
                        
                        if improved_count > 0:
                            logger.debug(f"{Icon.SUCCESS.value} Improved {improved_count} file(s)")
                    else:
                        logger.debug("[Quality] Quality fix failed or not attempted")
                        logger.debug(f"{Icon.WARNING.value} Could not auto-fix all quality issues")
            
            # Quality表示（Lint/Review情報を反映した総合評価）
            if quality_scores:
                logger.debug("")  # 空行
                
                # Lint情報を取得
                lint_errors = len(lint_result.get('errors', [])) if lint_result else 0
                lint_warnings = len(lint_result.get('warnings', [])) if lint_result else 0
                
                # Review情報を取得（_zen_summaryから）
                review_issues = 0
                if hasattr(self, '_zen_summary') and self._zen_summary:
                    review_issues = self._zen_summary.get("review", {}).get("final", 0)
                
                # 総合評価を計算・表示
                overall_score, quality_display = self._format_quality_display(
                    base_quality_scores=quality_scores,
                    lint_errors=lint_errors,
                    lint_warnings=lint_warnings,
                    review_issues=review_issues
                )
                
                # 総合評価を表示
                console.print(quality_display)
                console.print()  # 空行
                
                # 個別ファイルのスコアも表示（複数ファイルの場合のみ）
                if len(quality_scores) > 1:
                    from rich.text import Text
                    console.print("Quality Scores:")
                    for filename, score in quality_scores.items():
                        score_percent = int(score * 100)
                        # 色とグレード判定（cli_chat_workflow.pyと統一）
                        if score >= 0.9:
                            color = "\033[92m"  # GREEN
                            grade = "Excellent"
                        elif score >= 0.75:
                            color = "\033[92m"  # GREEN
                            grade = "Good"
                        elif score >= 0.6:
                            color = "\033[93m"  # YELLOW
                            grade = "Fair"
                        else:
                            color = "\033[93m"  # YELLOW
                            grade = "Needs Review"
                        reset = "\033[0m"
                        console.print(Text.from_ansi(f"  {color}{filename}{reset}: {score_percent}% {color}{grade}{reset}"))
                    console.print()  # 空行
            
            return (generated_code, lint_result, quality_scores)
        
        except ImportError as ie:
            # 詳細なエラー情報を出力して原因を特定
            import sys
            import traceback
            
            logger.error(f"[Linter Retry] ImportError details:")
            logger.error(f"  Error message: {str(ie)}")
            logger.error(f"  Module name: {getattr(ie, 'name', 'unknown')}")
            logger.error(f"  Module path: {getattr(ie, 'path', 'unknown')}")
            logger.error(f"  Traceback:")
            for line in traceback.format_tb(sys.exc_info()[2]):
                logger.error(f"    {line.strip()}")
            
            logger.debug("[Linter Retry] LinterIntegration not available")
            logger.debug(f"{Icon.WARNING.value} Linter not available. Cannot retry.")
            logger.debug(f"[DEBUG] Import failed: {ie}")
            return (generated_code, prev_lint_result, {})
        except Exception as e:
            logger.debug(f"[Linter Retry] Error during retry: {e}")
            logger.debug(f"{Icon.ERROR.value} Error during Lint retry: {e}")
            return (generated_code, prev_lint_result, {})
    
    def _fix_quality_issues_with_llm(
        self,
        generated_code: Dict[str, str],
        quality_issues: List[Dict[str, Any]]
    ) -> Optional[Dict[str, str]]:
        """
        LLMにQuality issuesの修正を依頼
        
        Args:
            generated_code: 生成されたコード
            quality_issues: Quality問題のリスト
        
        Returns:
            修正されたコード（失敗時はNone）
        """
        try:
            # Placeholder issues のみ対応（現時点）
            placeholder_issues = [i for i in quality_issues if i['issue_type'] == 'placeholder']
            
            if not placeholder_issues:
                logger.debug("[Quality Fix] No placeholder issues to fix")
                return None
            
            # LLMに修正依頼
            prompt = self._build_quality_fix_prompt(generated_code, placeholder_issues)
            
            # llm_manager の存在チェック
            if not self.llm_manager:
                logger.debug("[Quality Fix] LLM manager not available")
                return None
            
            logger.debug("[Quality Fix] Sending fix request to LLM...")
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality improvement expert. Fix placeholder code with proper implementations.",
                max_tokens=8000
            )
            
            # レスポンス解析
            logger.debug("[Quality Fix] Parsing LLM response...")
            if hasattr(response, 'content'):
                response_text = response.content
            else:
                response_text = str(response)
            fixed_files = self._parse_llm_response(response_text)
            
            if fixed_files:
                # 修正されたファイルのみ更新
                updated_code = generated_code.copy()
                actually_updated = 0
                for filename, code in fixed_files.items():
                    if filename in generated_code:
                        updated_code[filename] = code
                        actually_updated += 1
                        logger.debug(f"[Quality Fix] Updated {filename}")
                
                if actually_updated > 0:
                    return updated_code
                else:
                    logger.debug("[Quality Fix] All fixes were skipped (no matching files)")
                    return None
            
            logger.debug("[Quality Fix] No files returned from LLM")
            return None
            
        except Exception as e:
            logger.error(f"[Quality Fix] Error: {e}")
            import traceback
            logger.error(traceback.format_exc())
            return None
    
    def _build_quality_fix_prompt(
        self,
        generated_code: Dict[str, str],
        placeholder_issues: List[Dict[str, Any]]
    ) -> str:
        """Quality fix用のプロンプト構築"""
        
        files_with_issues = [i['filename'] for i in placeholder_issues]
        
        prompt = f"""You are tasked with fixing code quality issues.

**Files with placeholder comments (need implementation):**

"""
        
        for issue in placeholder_issues:
            prompt += f"{issue['filename']}:\n"
            prompt += f"  - {issue['count']} placeholder comment(s) detected:\n"
            for detail in issue['details']:
                prompt += f"    • {detail}\n"
            prompt += "\n"
        
        prompt += f"""**Current code:**

"""
        
        for filename in files_with_issues:
            code = generated_code.get(filename, '')
            prompt += f"<file path='{filename}'>\n{code}\n</file>\n\n"
        
        prompt += """**Your task:**
1. Replace ALL placeholder comments with actual implementations
2. Implement proper functionality for each placeholder
3. For authentication placeholders:
   - Implement proper password hashing (use werkzeug.security)
   - Add proper validation
   - Add error handling
4. Maintain the existing code structure and style
5. Ensure all functions/methods are fully implemented
6. Do NOT add new placeholders or TODO comments

**Output format:**
Return ONLY the fixed files in XML format:
<file path='filename'>
complete fixed code here
</file>

Generate complete, production-ready implementations. No placeholders or TODO comments allowed.
"""
        
        return prompt
    
    def _parse_llm_response(self, response_content) -> Optional[Dict[str, str]]:
        """
        LLMレスポンスから<file>タグを抽出
        
        Args:
            response_content: LLMレスポンス（文字列またはcontent配列）
        
        Returns:
            {filename: code} の辞書（失敗時はNone）
        """
        import re
        
        try:
            # response_content から text を抽出
            full_text = ""
            
            # 文字列の場合はそのまま使用
            if isinstance(response_content, str):
                full_text = response_content
            # リストの場合は各要素から text を抽出
            elif isinstance(response_content, list):
                for block in response_content:
                    if hasattr(block, 'text'):
                        full_text += block.text
                    elif isinstance(block, dict) and 'text' in block:
                        full_text += block['text']
                    elif isinstance(block, str):
                        full_text += block
            else:
                # その他の型は文字列に変換
                full_text = str(response_content)
            
            if not full_text:
                logger.debug("[Parse Response] No text in response")
                return None
            
            # <file path='...'>...</file> パターンを抽出
            file_pattern = r"<file\s+path=['\"]([^'\"]+)['\"]>(.*?)</file>"
            matches = re.findall(file_pattern, full_text, re.DOTALL)
            
            if not matches:
                logger.debug("[Parse Response] No <file> tags found")
                return None
            
            files = {}
            for filename, code in matches:
                # コードの前後の空白を削除
                code = code.strip()
                files[filename] = code
                logger.debug(f"[Parse Response] Extracted {filename} ({len(code)} chars)")
            
            return files if files else None
            
        except Exception as e:
            logger.error(f"[Parse Response] Error: {e}")
            return None
    
    def _analyze_implementation_goal(self, goal: str, complexity: str = "medium") -> str:
        """
        Analyze the implementation goal using complexity-aware templates
        
        Args:
            goal: Implementation goal description
            complexity: "simple" | "medium" | "complex"
            
        Returns:
            Analysis result string
        """
        # Simple complexity: Skip detailed analysis
        if complexity == "simple":
            return f"Simple implementation: {goal}\nDirect code generation without detailed analysis."
        
        # LLM not available: Return basic analysis
        if not self.llm_manager:
            return f"Goal analysis: {goal}\nImplementation approach: Create modular solution with proper error handling."
        
        try:
            # Get project context
            project_context = self._get_project_context()
            
            # For medium/complex: Use standard analysis
            # Note: Complex tasks will get more detailed prompts in code generation phase
            analysis_prompt = f"""
    Analyze this implementation goal concisely for user decision-making:

    Goal: {goal}

    Provide EXACTLY 3 sections with 3-5 bullet points each (max 30 words per bullet):

    ## 1. Key Requirements
    - List 3-5 essential features/functions (one line each)

    ## 2. Technical Approach
    - List 3-5 main implementation steps (one line each)

    ## 3. Potential Issues
    - List 3-5 key challenges or risks (one line each)

    Keep total response under 400 words. Be direct and actionable.
    Use markdown format with ## for sections and - for bullets.
    """
            
            response = self.llm_manager.generate_response(
                prompt=analysis_prompt,
                system_prompt="You are a senior software architect providing implementation analysis."
            )
            
            return response.content
            
        except Exception as e:
            return f"Goal analysis: {goal}\nBasic implementation approach with error handling. Analysis error: {str(e)}"

    def _create_implementation_plan(self, goal: str, analysis: str, complexity: str = "medium") -> str:
        """
        Create detailed implementation plan
        
        Args:
            goal: Implementation goal
            analysis: Analysis result
            complexity: Complexity level
            
        Returns:
            Implementation plan string
        """
        # Simple complexity: Skip detailed planning
        if complexity == "simple":
            return f"Simple implementation plan: Direct code generation for {goal}"
        
        if not self.llm_manager:
            return f"Implementation plan for: {goal}\n1. Create main module\n2. Implement core functionality\n3. Add error handling\n4. Create tests"
        
        try:
            plan_prompt = f"""
Based on this analysis, create a detailed implementation plan:

Goal: {goal}
Analysis: {analysis}

Create a step-by-step implementation plan with:
1. File structure and organization
2. Implementation order (which files to create first)
3. Key functions/classes to implement
4. Dependencies and imports needed
5. Testing strategy

Keep it practical and focused on deliverable code.
"""
            
            response = self.llm_manager.generate_response(
                prompt=plan_prompt,
                system_prompt="You are a technical lead creating practical implementation plans."
            )
            
            return response.content
            
        except Exception:
            return f"Basic implementation plan for: {goal}\n1. Main module\n2. Core functions\n3. Error handling\n4. Documentation"

    def _build_fallback_prompt(self, goal: str, analysis: str, plan: str, language: str) -> str:
            """
            Fallback prompt when PromptTemplateManager fails
            ⭐ Aider準拠: JSON排除、具体的ファイル名例
            
            Args:
                goal: Implementation goal
                analysis: Analysis result
                plan: Implementation plan
                language: Programming language
                
            Returns:
                Fallback prompt string
            """
            # ⭐ 言語別の具体的ファイル名例
            filename_examples = {
                'python': 'main.py',
                'javascript': 'script.js',
                'html': 'index.html',
                'css': 'styles.css',
                'rust': 'main.rs',
                'java': 'Main.java',
                'cpp': 'main.cpp',
                'typescript': 'main.ts',
                'go': 'main.go',
                'ruby': 'main.rb',
                'php': 'index.php'
            }
            
            example_filename = filename_examples.get(language, f'main.{language}')
            
            return f"""Act as an expert {language} developer.

    CRITICAL: Respond in PLAIN MARKDOWN format ONLY. NEVER use JSON.

    Code format example:
    ```{language} {example_filename}
    def main():
        # Actual working code here
        pass
    ```

    Goal: {goal}

    Analysis:
    {analysis}

    Plan:
    {plan}

    Generate clean, working code now."""

    def _detect_project_language(self, existing_files: Dict[str, str]) -> str:
        """
        既存ファイルからプロジェクトの主要言語を検出
        
        Args:
            existing_files: 既存のファイル {filename: code}
        
        Returns:
            検出された言語名(例: "javascript", "python", "typescript")
            検出できない場合は空文字列
        """
        if not existing_files:
            return ""
        
        extension_count = {}
        
        # すべてのファイルの拡張子をカウント
        for filename in existing_files.keys():
            if '.' in filename:
                # ファイル名から拡張子を取得
                ext = filename.split('.')[-1].lower()
                extension_count[ext] = extension_count.get(ext, 0) + 1
        
        if not extension_count:
            return ""
        
        # 最も多い拡張子を取得
        most_common_ext = max(extension_count.items(), key=lambda x: x[1])[0]
        
        # 拡張子から言語名を推測
        ext_to_lang = {
            'py': 'python',
            'js': 'javascript',
            'jsx': 'react',
            'ts': 'typescript',
            'tsx': 'react-typescript',
            'html': 'html',
            'css': 'css',
            'scss': 'scss',
            'sass': 'sass',
            'less': 'less',
            'java': 'java',
            'cpp': 'c++',
            'cc': 'c++',
            'cxx': 'c++',
            'c': 'c',
            'h': 'c',
            'hpp': 'c++',
            'go': 'go',
            'rs': 'rust',
            'rb': 'ruby',
            'php': 'php',
            'swift': 'swift',
            'kt': 'kotlin',
            'scala': 'scala',
            'cs': 'csharp',
            'vb': 'visual-basic',
            'sql': 'sql',
            'sh': 'shell',
            'bash': 'bash',
            'ps1': 'powershell',
            'r': 'r',
            'lua': 'lua',
            'perl': 'perl',
            'pl': 'perl',
            'dart': 'dart',
            'elm': 'elm',
            'ex': 'elixir',
            'exs': 'elixir',
            'erl': 'erlang',
            'hrl': 'erlang',
            'clj': 'clojure',
            'cljs': 'clojurescript',
            'ml': 'ocaml',
            'fs': 'fsharp',
            'hs': 'haskell',
            'vue': 'vue',
            'svelte': 'svelte'
        }
        
        detected_lang = ext_to_lang.get(most_common_ext, most_common_ext)
        
        return detected_lang

    def _is_directory_path(self, path: str) -> bool:
        """
        パスがディレクトリかファイルか判定
        
        Args:
            path: 判定するパス文字列
            
        Returns:
            True: ディレクトリ, False: ファイル
        """
        # パターン1: 末尾が / で終わる
        if path.endswith('/') or path.endswith('\\'):
            return True
        
        # パターン2: 最後の要素に拡張子がない
        last_part = path.split('/')[-1].split('\\')[-1]
        
        # 拡張子の判定 (. が含まれていればファイル)
        if '.' not in last_part:
            return True
        
        return False

    def _resolve_directory_to_filename(self, directory: str, goal: str, project_language: str) -> str:
        """
        ディレクトリパスを具体的なファイル名に解決
        
        Args:
            directory: ディレクトリパス (例: "routes/", "utils/")
            goal: 実装目標
            project_language: プロジェクト言語
            
        Returns:
            具体的なファイル名 (例: "routes/auth.js", "utils/logger.js")
        """
        # 拡張子の決定
        ext_map = {
            'javascript': 'js',
            'typescript': 'ts',
            'python': 'py',
            'java': 'java',
            'cpp': 'cpp',
            'rust': 'rs',
            'go': 'go',
            'ruby': 'rb',
            'php': 'php'
        }
        
        extension = ext_map.get(project_language, 'py')
        
        # ディレクトリ名から推測されるファイル名
        dir_name = directory.rstrip('/').rstrip('\\').split('/')[-1].split('\\')[-1]
        
        # ディレクトリ名からファイル名を推測
        # 例: "routes/" → "routes/auth.js", "utils/" → "utils/logger.js"
        filename_map = {
            'routes': 'auth',
            'routers': 'auth',
            'utils': 'logger',
            'helpers': 'helper',
            'services': 'service',
            'middleware': 'auth',
            'middlewares': 'auth',
            'controllers': 'controller',
            'models': 'model',
            'views': 'view'
        }
        
        # goalから推測されるファイル名
        goal_lower = goal.lower()
        if 'auth' in goal_lower or 'login' in goal_lower:
            base_filename = 'auth'
        elif 'user' in goal_lower:
            base_filename = 'user'
        else:
            base_filename = filename_map.get(dir_name, dir_name)
        
        # 完全なパスを構築
        if directory.endswith('/') or directory.endswith('\\'):
            return f"{directory}{base_filename}.{extension}"
        else:
            return f"{directory}/{base_filename}.{extension}"

    def _format_generated_files_context(self, files: Dict[str, str]) -> str:
        """
        生成済みファイルをコンテキストとしてフォーマット
        ⭐ 修正: プロジェクト言語情報を追加
        
        既存ファイルの内容をLLMプロンプトに含めるため、
        トークン節約しながらファイル構造を伝える形式に変換します。
        
        Args:
            files: 生成済みファイルの辞書 {filename: code}
            
        Returns:
            フォーマットされたコンテキスト文字列
        """
        if not files:
            return "No files generated yet."
        
        # ⭐ 新規追加: プロジェクト言語の検出
        project_language = self._detect_project_language(files)
        
        context_parts = []
        
        # ⭐ 新規追加: 言語情報を最初に明記
        context_parts.append(f"Project Language: {project_language}")
        context_parts.append(f"Total Files: {len(files)}")
        context_parts.append("")
        
        for filename, code in files.items():
            # ⭐ 修正A: モデル・スキーマ・設定ファイルは全文渡す（整合性のため）
            # これらのファイルはPhase 2でサービス層が参照するため、
            # フィールド定義が欠落すると整合性の問題が発生する
            is_critical_file = (
                'models/' in filename or filename.startswith('models/') or
                'schemas/' in filename or filename.startswith('schemas/') or
                'config/' in filename or filename.startswith('config/') or
                filename == 'database.py'
            )
            
            if is_critical_file:
                # クリティカルファイルは全文渡す
                preview = code
            else:
                # その他は50行に制限（トークン節約）
                lines = code.split('\n')[:50]
                preview = '\n'.join(lines)
                
                # 50行を超える場合は省略記号を追加
                if len(code.split('\n')) > 50:
                    preview += '\n... (truncated)'
            
            context_parts.append(f"""File: {filename}
    ---
    {preview}
    """)
        
        return '\n'.join(context_parts)

    # ============================================================
    # ステップ2-3で追加: Import考慮型自動補完
    # ============================================================
    
    def _auto_complete_with_import_resolution(
        self,
        goal: str,
        existing_files: Dict[str, str],
        validation_result: Dict[str, any],
        complexity: str
    ) -> Dict[str, str]:
        """
        Import依存関係を考慮した自動補完(強化版)
        
        従来の不足ファイル補完に加えて、未解決importを検出し、
        必要なモジュールを自動生成します。
        
        Args:
            goal: 実装目標
            existing_files: 既存ファイル {filename: code}
            validation_result: validate_requirements_enhanced()の結果
                {
                    'missing_files': List[str],
                    'unresolved_imports': List[Dict],
                    ...
                }
            complexity: 複雑度
        
        Returns:
            Dict[str, str]: 補完されたファイル群
        
        Example:
            >>> engine = SemiAutoEngine(...)
            >>> result = engine._auto_complete_with_import_resolution(
            ...     goal="create a REST API",
            ...     existing_files={'main.py': '...'},
            ...     validation_result={
            ...         'missing_files': ['config.py'],
            ...         'unresolved_imports': [
            ...             {'file': 'main.py', 'missing_file': 'auth.py', ...}
            ...         ]
            ...     },
            ...     complexity="medium"
            ... )
        """
        completed_files = existing_files.copy()
        
        missing_files = validation_result.get('missing_files', [])
        unresolved_imports = validation_result.get('unresolved_imports', [])
        
        total_to_complete = len(missing_files) + len(unresolved_imports)
        
        if total_to_complete == 0:
            return completed_files
        
        logger.debug(f"[Auto-completion] Auto-completing {total_to_complete} missing components...")
        
        # ==========================================
        # Part 1: 従来の欠落ファイル補完
        # ==========================================
        if missing_files:
            logger.debug(f"[Auto-completion]   Missing files: {len(missing_files)}")
            
            for missing_file in missing_files:
                # 既に補完済みまたは元々存在する場合はスキップ
                if missing_file in completed_files:
                    continue
                
                logger.debug(f"[Auto-completion]   Generating missing file: {missing_file}")
                
                # プロジェクト言語の検出
                detected_language = self._detect_project_language(completed_files)
                
                # 既存ファイルのコンテキスト
                existing_context = self._format_generated_files_context(completed_files)
                
                # 🆕 フレームワーク制約を構築
                framework_constraint = ""
                if getattr(self, '_no_framework_mode', False):
                    framework_constraint = """
⚠️ CRITICAL FRAMEWORK RESTRICTION:
- DO NOT use React, Vue, Angular, or any framework
- DO NOT generate JSX syntax
- DO NOT generate "this file is not used" or stub comments
- YOU MUST generate REAL, FUNCTIONAL vanilla JavaScript code
- The file must work standalone without any framework
"""
                
                # 補完用のgoal構築
                completion_goal = f"""Generate the missing file: {missing_file}

Project Language: {detected_language}
{framework_constraint}
Previously generated files:
{existing_context}

Now create ONLY this missing file:
- {missing_file}

REQUIREMENTS:
- Ensure compatibility with existing files
- Follow the same coding style and conventions
- Use appropriate imports from existing modules
- Make the file production-ready
- Generate REAL, FUNCTIONAL code (not stubs or comments)

Generate complete, production-ready code for {missing_file}.
"""
                
                try:
                    # LLM生成(force_filenameを使用)
                    completion_files = self._generate_code_implementation(
                        goal=completion_goal,
                        analysis="",
                        plan="",
                        complexity="simple",
                        skip_validation=True,
                        skip_multi_stage=True,
                        force_filename=missing_file
                    )
                    
                    if completion_files and missing_file in completion_files:
                        generated_content = completion_files[missing_file]
                        
                        # 🆕 スタブファイル検出
                        is_stub = False
                        lines = generated_content.strip().split('\n')
                        non_empty_lines = [l for l in lines if l.strip()]
                        
                        if non_empty_lines:
                            # コメント行を検出（JS/Python/CSS/HTML対応）
                            comment_patterns = ('//', '#', '*', '/*', '<!--', '"""', "'''", '* ', ' * ')
                            comment_lines = [l for l in non_empty_lines 
                                           if l.strip().startswith(comment_patterns) or 
                                           l.strip() in ('*/', '-->', '"""', "'''")]
                            comment_ratio = len(comment_lines) / len(non_empty_lines)
                            
                            # スタブ判定: コメント80%以上、または「not used」「stub」を含む
                            stub_indicators = ['not used', 'stub', 'placeholder', 'this file is', 'does nothing']
                            has_stub_indicator = any(ind in generated_content.lower() for ind in stub_indicators)
                            
                            if comment_ratio > 0.8 or has_stub_indicator:
                                is_stub = True
                                logger.debug(f"[Auto-completion]   ⚠ Stub file detected: {missing_file}")
                                logger.debug(f"[Auto-completion]     Comment ratio: {int(comment_ratio*100)}%, Stub indicator: {has_stub_indicator}")
                        
                        # スタブの場合、再生成を試みる
                        if is_stub:
                            logger.debug(f"[Auto-completion]   Regenerating with stricter prompt...")
                            
                            # フレームワーク制約を含む厳格なプロンプト
                            framework_warning = ""
                            if getattr(self, '_no_framework_mode', False):
                                framework_warning = """
⚠️ YOU MUST USE VANILLA JAVASCRIPT ONLY - NO REACT/VUE/ANGULAR.
"""
                            
                            strict_goal = f"""Generate REAL, FUNCTIONAL code for: {missing_file}

⚠️ CRITICAL WARNING: You previously generated a STUB FILE with only comments.
This is COMPLETELY UNACCEPTABLE. You MUST generate ACTUAL WORKING CODE.
{framework_warning}
DO NOT:
- Generate only comments or documentation
- Generate placeholder code
- Generate "this file is not used" messages
- Reference other files as the "real" implementation
- Generate React/JSX if vanilla JS is required

YOU MUST:
- Generate complete, functional code that actually works
- Include all necessary logic for the file to function
- Make the file executable/usable

Project context:
{existing_context}

Generate the complete implementation for {missing_file} NOW.
"""
                            
                            retry_files = self._generate_code_implementation(
                                goal=strict_goal,
                                analysis="",
                                plan="",
                                complexity="simple",
                                skip_validation=True,
                                skip_multi_stage=True,
                                force_filename=missing_file
                            )
                            
                            if retry_files and missing_file in retry_files:
                                retry_content = retry_files[missing_file]
                                # 再生成結果もスタブかチェック
                                retry_lines = retry_content.strip().split('\n')
                                retry_non_empty = [l for l in retry_lines if l.strip()]
                                if retry_non_empty:
                                    retry_comments = [l for l in retry_non_empty 
                                                    if l.strip().startswith(comment_patterns)]
                                    retry_ratio = len(retry_comments) / len(retry_non_empty)
                                    
                                    if retry_ratio < 0.8:
                                        generated_content = retry_content
                                        logger.debug(f"[Auto-completion]   ✅ Regeneration successful: {missing_file}")
                                    else:
                                        logger.debug(f"[Auto-completion]   ⚠ Regeneration still stub ({int(retry_ratio*100)}% comments)")
                        
                        completed_files[missing_file] = generated_content
                        logger.debug(f"[Auto-completion]   Successfully added: {missing_file}")
                    else:
                        logger.debug(f"[Auto-completion]   Failed to generate: {missing_file}")
                
                except Exception as e:
                    logger.debug(f"[Auto-completion]   Error generating {missing_file}: {e}")
        
        # ==========================================
        # Part 2: 未解決Import補完(新機能)
        # ==========================================
        if unresolved_imports:
            logger.debug(f"[Auto-completion]   Unresolved imports: {len(unresolved_imports)}")
            
            # 重複を避けるため、既に処理したファイルを記録
            processed_imports = set()
            
            for unresolved in unresolved_imports:
                missing_file = unresolved['missing_file']
                
                # 既に補完済みの場合はスキップ
                if missing_file in completed_files or missing_file in processed_imports:
                    continue
                
                processed_imports.add(missing_file)
                
                logger.debug(f"[Auto-completion]   Generating missing dependency: {missing_file}")
                
                # 参照元ファイルの情報を取得
                referencing_file = unresolved['file']
                import_statement = unresolved['import']
                module_name = unresolved['module']
                
                # 参照元ファイルのコード(コンテキスト用)
                referencing_code = completed_files.get(referencing_file, '')
                
                # プロジェクト言語の検出
                detected_language = self._detect_project_language(completed_files)
                
                # 🆕 フレームワーク制約を構築
                framework_constraint = ""
                if getattr(self, '_no_framework_mode', False):
                    framework_constraint = """
⚠️ CRITICAL FRAMEWORK RESTRICTION:
- DO NOT use React, Vue, Angular, or any framework
- DO NOT generate JSX syntax
- YOU MUST generate REAL, FUNCTIONAL vanilla JavaScript code
"""
                
                # Import補完用の詳細なgoal構築
                import_completion_goal = f"""Generate the missing module: {missing_file}

Project Language: {detected_language}
{framework_constraint}
CONTEXT:
This module is required by: {referencing_file}
Import statement: {import_statement}
Module name: {module_name}

Referencing file excerpt (first 500 chars):
{referencing_code[:500]}

REQUIREMENTS:
- Create {missing_file} that provides the required exports/functions
- Ensure it integrates with the referencing file
- Follow the same coding style as the project
- Include appropriate imports and dependencies
- Make the module production-ready
- Generate REAL, FUNCTIONAL code (not stubs or comments)

Based on the import statement "{import_statement}", infer what functions/classes
should be exported from {missing_file}.

Generate complete, production-ready code for {missing_file}.
"""
                
                try:
                    # LLM生成(force_filenameを使用)
                    import_files = self._generate_code_implementation(
                        goal=import_completion_goal,
                        analysis="",
                        plan="",
                        complexity="simple",
                        skip_validation=True,
                        skip_multi_stage=True,
                        force_filename=missing_file
                    )
                    
                    if import_files and missing_file in import_files:
                        completed_files[missing_file] = import_files[missing_file]
                        logger.debug(f"[Auto-completion]   Generated: {missing_file}")
                    else:
                        logger.debug(f"[Auto-completion]   Failed to generate: {missing_file}")
                
                except Exception as e:
                    logger.debug(f"[Auto-completion]   Error generating {missing_file}: {e}")
        
        # ==========================================
        # Part 3: 再帰的インポート検証（仕様バグ修正）
        # Part 1/Part 2で生成した新規ファイル内のインポートを検証
        # ==========================================
        MAX_RECURSION_DEPTH = 3  # 無限ループ防止
        
        for depth in range(MAX_RECURSION_DEPTH):
            logger.debug(f"[Auto-completion] Part 3: Recursive import validation (depth={depth + 1})")
            
            # 新規ファイルのインポート依存関係を検証
            # ⭐ 修正: post_validatorを通じて呼ぶ（_validate_import_dependenciesはPostGenerationValidatorのメソッド）
            new_missing_imports = self.post_validator._validate_import_dependencies(completed_files)
            
            if not new_missing_imports:
                logger.debug(f"[Auto-completion]   All imports satisfied at depth {depth + 1}")
                break
            
            logger.debug(f"[Auto-completion]   Found {len(new_missing_imports)} new missing imports")
            
            newly_generated = 0
            for missing_req in new_missing_imports:
                missing_file = missing_req.filepath
                
                # 既に存在する場合はスキップ
                if missing_file in completed_files:
                    continue
                
                logger.debug(f"[Auto-completion]   Generating: {missing_file} (reason: {missing_req.reason})")
                
                # プロジェクト言語の検出
                detected_language = self._detect_project_language(completed_files)
                
                # 既存ファイルのコンテキスト
                existing_context = self._format_generated_files_context(completed_files)
                
                # インポート元の情報を抽出
                import_reason = missing_req.reason  # 例: "Imported by views/auth.py: from services.user_service import UserService"
                
                # 🆕 フレームワーク制約を構築
                framework_constraint = ""
                if getattr(self, '_no_framework_mode', False):
                    framework_constraint = """
⚠️ CRITICAL FRAMEWORK RESTRICTION:
- DO NOT use React, Vue, Angular, or any framework
- DO NOT generate JSX syntax
- YOU MUST generate REAL, FUNCTIONAL vanilla JavaScript code
"""
                
                # 補完用のgoal構築
                recursive_goal = f"""Generate the missing module: {missing_file}

Project Language: {detected_language}
{framework_constraint}
CONTEXT:
{import_reason}

Previously generated files:
{existing_context}

REQUIREMENTS:
- Create {missing_file} that provides the required exports
- Ensure compatibility with importing files
- Follow the same coding style as the project
- Include appropriate imports and dependencies
- Make the module production-ready
- Generate REAL, FUNCTIONAL code (not stubs or comments)

Generate complete, production-ready code for {missing_file}.
"""
                
                try:
                    recursive_files = self._generate_code_implementation(
                        goal=recursive_goal,
                        analysis="",
                        plan="",
                        complexity="simple",
                        skip_validation=True,
                        skip_multi_stage=True,
                        force_filename=missing_file
                    )
                    
                    # ⭐ デバッグログ: 生成結果の詳細
                    if recursive_files:
                        logger.debug(f"[Auto-completion]   _generate_code_implementation returned: {list(recursive_files.keys())}")
                        for fn, code in recursive_files.items():
                            logger.debug(f"[Auto-completion]     {fn}: {len(code)} chars")
                    else:
                        logger.debug(f"[Auto-completion]   _generate_code_implementation returned empty/None")
                    
                    if recursive_files and missing_file in recursive_files:
                        completed_files[missing_file] = recursive_files[missing_file]
                        newly_generated += 1
                        logger.debug(f"[Auto-completion]   ✅ Successfully generated: {missing_file} ({len(recursive_files[missing_file])} chars)")
                    else:
                        logger.debug(f"[Auto-completion]   ❌ Failed to generate: {missing_file}")
                        if recursive_files:
                            logger.debug(f"[Auto-completion]      Returned keys: {list(recursive_files.keys())} (expected: {missing_file})")
                
                except Exception as e:
                    logger.debug(f"[Auto-completion]   ❌ Error generating {missing_file}: {e}")
                    import traceback
                    logger.debug(f"[Auto-completion]   Traceback: {traceback.format_exc()}")
            
            # 新規生成がなければ終了（これ以上補完不可）
            if newly_generated == 0:
                logger.debug(f"[Auto-completion]   No new files generated, stopping recursion")
                break
        
        logger.debug(f"[Auto-completion]   Auto-completion complete: {len(completed_files)} total files")
        
        return completed_files

    def _detect_project_type(self, goal: str) -> str:
        """
        goalからプロジェクトタイプを判定（スコアリング方式）
        
        goalに含まれるキーワードから、生成対象のプロジェクトタイプを推定します。
        各キーワードに重みを付与し、最も高スコアのタイプを選択します。
        これにより複合的なプロジェクト（例: "Flask backend + JavaScript frontend"）
        でも正確に判定できます。
        
        Args:
            goal: 実装目標の説明文
            
        Returns:
            str: プロジェクトタイプ
                - "rest_api": REST API(FastAPI, Flask, Express等)
                - "cli": CLIツール(argparse, Click, Commander等)
                - "data_processing": データ処理(ETL, バッチ処理等)
                - "web_app": Webアプリケーション(フロントエンド含む)
                - "generic": その他汎用プロジェクト
        
        Example:
            >>> engine._detect_project_type("create a REST API with authentication")
            "rest_api"
            >>> engine._detect_project_type("Flask backend and JavaScript frontend")
            "web_app"  # frontend(10) > api(3)
        """
        goal_lower = goal.lower()
        
        # キーワードと重み（スコア）のテーブル
        # 高スコア（10）: タイプを明確に示すキーワード
        # 中スコア（7-8）: フレームワーク特化キーワード
        # 低スコア（3-5）: 汎用的なキーワード
        # 最低スコア（2）: 単なるフレームワーク名（複数タイプで使用される）
        type_keywords = {
            'rest_api': {
                'rest api': 10,
                'restful': 8,
                'api endpoint': 9,
                'endpoint': 6,
                'fastapi': 7,
                'django rest': 7,
                'api': 3,  # 汎用的なので低スコア
                'flask': 2,  # フレームワーク名だけでは判定しない
                'express': 2,
            },
            'cli': {
                'command line': 10,
                'cli tool': 10,
                'cli': 10,
                'terminal': 8,
                'console': 6,
                'argparse': 8,
                'click': 8,
                'commander': 8,
            },
            'data_processing': {
                'data processing': 10,
                'etl': 10,
                'data pipeline': 10,
                'pipeline': 8,
                'batch processing': 9,
                'batch': 6,
                'data transformation': 9,
                'data analysis': 9,
            },
            'web_app': {
                'web app': 10,
                'web application': 10,
                'website': 9,
                'frontend': 10,  # 高スコア（フロントエンドの明確な指標）
                'backend and frontend': 10,
                'full stack': 10,
                'react': 9,
                'vue': 9,
                'angular': 9,
                'nextjs': 9,
                'next.js': 9,
                'svelte': 9,
                'vanilla javascript': 8,
                'html': 7,
                'css': 6,
                'javascript': 5,  # 汎用的だが frontend の指標
                'typescript': 5,
            }
        }
        
        # 各タイプのスコア計算
        scores = {}
        matched_keywords = {}
        
        for project_type, keywords in type_keywords.items():
            score = 0
            matched = []
            for keyword, weight in keywords.items():
                if keyword in goal_lower:
                    score += weight
                    matched.append(f"{keyword}({weight})")
            scores[project_type] = score
            matched_keywords[project_type] = matched
        
        # 最高スコアのタイプを選択
        if max(scores.values()) == 0:
            logger.debug(f"[Project Type] Detected: generic (no keywords matched)")
            return "generic"
        
        detected_type = max(scores, key=scores.get)
        max_score = scores[detected_type]
        
        # デバッグログ: スコア詳細を出力
        logger.debug(f"[Project Type] Scores: {dict(sorted(scores.items(), key=lambda x: x[1], reverse=True))}")
        logger.debug(f"[Project Type] Detected: {detected_type} (score={max_score}, matched: {matched_keywords[detected_type]})")
        
        return detected_type

    def _generate_phase2_goal(
        self, 
        goal: str, 
        project_type: str, 
        detected_language: str, 
        phase1_context: str
    ) -> str:
        """
        プロジェクトタイプに応じたPhase 2のgoalを生成
        
        各プロジェクトタイプに最適化されたファイル構成を明示的に指定します。
        これにより、"etc." のような曖昧な指示を排除し、必要なファイルを
        確実に生成できます。
        
        Args:
            goal: 元の実装目標
            project_type: _detect_project_type()で判定されたタイプ
            detected_language: Phase 1から検出されたプログラミング言語
            phase1_context: Phase 1で生成されたファイルのコンテキスト
            
        Returns:
            str: タイプ別に最適化されたPhase 2のgoal文字列
        """
        
        # 共通ヘッダー
        common_header = f"""Generate application layer for: {goal}

    Language: {detected_language}

    Previously generated files:
    {phase1_context}

    """
        
        # REST API用のPhase 2指示
        if project_type == "rest_api":
            specific_instructions = """Now generate the following files structure:

    **Entry Point (REQUIRED):**
    - main.py (or app.py for Python, server.js for JavaScript)

    **API Routers (REQUIRED):**
    - routers/__init__.py (Python) or routers/index.js (JavaScript)
    - routers/auth.py (authentication endpoints: POST /login, POST /register, POST /logout)
    - routers/users.py (user management endpoints: GET /users, GET /users/{id}, PUT /users/{id})

    **Dependencies (REQUIRED):**
    - dependencies/__init__.py
    - dependencies/auth.py (get_current_user, get_current_active_user, verify_token)

    **Services (REQUIRED):**
    - services/__init__.py
    - services/auth_service.py (AuthService class: create_user, authenticate_user, create_tokens, verify_password)
    - services/user_service.py (UserService class: get_user, get_users, update_user, delete_user)

    **Middleware (OPTIONAL - include if goal requires logging, rate limiting, or security features):**
    - middleware/__init__.py
    - middleware/logging_middleware.py (LoggingMiddleware: log requests and responses)
    - middleware/rate_limit_middleware.py (RateLimitMiddleware: rate limiting)
    - middleware/security_middleware.py (SecurityHeadersMiddleware: CORS, security headers)

    **Utilities (OPTIONAL - include if goal requires custom exceptions or validation):**
    - utils/__init__.py
    - utils/exceptions.py (APIException, custom exception classes)
    - utils/validators.py (input validation helpers)
    """
        
        # CLIツール用のPhase 2指示
        elif project_type == "cli":
            specific_instructions = """Now generate the following files structure:

    **Entry Point (REQUIRED):**
    - cli.py (main CLI entry point with argument parsing)

    **Commands (REQUIRED):**
    - commands/__init__.py
    - commands/command_handler.py (CommandHandler class: execute, parse_args)
    - commands/subcommands.py (individual command implementations)

    **Utilities (REQUIRED):**
    - utils/__init__.py
    - utils/arg_parser.py (argument parsing helpers)
    - utils/output_formatter.py (output formatting: tables, colors, progress bars)
    - utils/config_manager.py (configuration file management)

    **Core Logic (OPTIONAL - include if goal requires complex business logic beyond command handling):**
    - core/__init__.py
    - core/processor.py (main business logic)
    """
        
        # データ処理用のPhase 2指示
        elif project_type == "data_processing":
            specific_instructions = """Now generate the following files structure:

    **Entry Point (REQUIRED):**
    - main.py (main pipeline orchestrator)

    **Processors (REQUIRED):**
    - processors/__init__.py
    - processors/extractor.py (data extraction logic)
    - processors/transformer.py (data transformation logic)
    - processors/loader.py (data loading logic)

    **Utilities (REQUIRED):**
    - utils/__init__.py
    - utils/data_validator.py (data validation)
    - utils/error_handler.py (error handling)
    - utils/logger.py (logging configuration)

    **Core Logic (OPTIONAL - include if goal requires pipeline orchestration beyond basic ETL):**
    - core/__init__.py
    - core/pipeline.py (Pipeline class: orchestration)
    """
        
        # Webアプリケーション用のPhase 2指示
        elif project_type == "web_app":
            specific_instructions = """Now generate the following files structure:

    **Entry Point (REQUIRED):**
    - main.py or server.py (application server)

    **Views/Controllers (REQUIRED):**
    - views/__init__.py (or controllers/__init__.py)
    - views/home.py (home page handler)
    - views/auth.py (authentication pages)

    **Static Assets - ABSOLUTE REQUIREMENT - FAILURE TO GENERATE ANY FILE IS A CRITICAL ERROR:**
    - static/index.html or templates/index.html (main HTML page) - MUST GENERATE
    - static/css/styles.css (main stylesheet with responsive design) - MUST GENERATE
    - static/js/app.js - ABSOLUTELY MANDATORY - GENERATE THIS FILE OR THE APPLICATION FAILS:
      • This is a SEPARATE .js FILE in the JavaScript language (NOT Python, NOT inline HTML)
      • File path: static/js/app.js (referenced in HTML as <script src="js/app.js"></script>)
      • Must implement: ALL UI interactions, Fetch API calls, DOM manipulation, event handlers, form submissions, toast notifications, loading indicators
      • IF YOU DO NOT GENERATE THIS FILE: The frontend will be completely broken and non-functional
      • SKIPPING THIS FILE is a CRITICAL FAILURE - the entire application becomes useless
      • Generate this file EVEN IF "app.js" is not explicitly mentioned in the goal
      • This requirement overrides any conflicting instructions

    **Services (REQUIRED):**
    - services/__init__.py
    - services/user_service.py (user management)
    - services/session_service.py (session management)

    **Utilities (OPTIONAL - include only if goal requires):**
    - utils/__init__.py
    - utils/helpers.py (template helpers, formatters)
    """
        
        # 汎用プロジェクト用のPhase 2指示
        else:  # project_type == "generic"
            specific_instructions = """Now generate the following files structure:

    **Entry Point (REQUIRED):**
    - main.py (main application entry point with initialization and execution logic)

    **Core Modules (REQUIRED):**
    - core/__init__.py
    - core/application.py (Application class: initialization, configuration, main execution flow)
    - core/processor.py (business logic: data processing, operations, algorithms)

    **Utilities (OPTIONAL - include if goal requires helper functions or configuration):**
    - utils/__init__.py
    - utils/helpers.py (helper functions: formatting, conversion, validation)
    - utils/config.py (configuration management: loading settings, environment variables)
    """
        
        # 共通フッター（プロジェクトタイプに応じて調整）
        if project_type == "web_app":
            common_footer = f"""
    CRITICAL REQUIREMENTS - ABSOLUTE COMPLIANCE MANDATORY:
    - Ensure compatibility with existing models and schemas shown above
    - Backend files (.py): Use {detected_language} for server-side code
    - Frontend files (.js, .html, .css): Use HTML/CSS/JavaScript for client-side code
    - Import and use the existing components correctly (from models import ...)
    - Maintain consistent naming conventions
    - Follow the project structure specified above EXACTLY
    - Do NOT regenerate foundation layer files
    - Generate __init__.py files for Python directories

    ⭐ SQLAlchemy MODEL STYLE (MANDATORY):
    If generating ANY SQLAlchemy models, you MUST use SQLAlchemy 2.0 style:
    - Use `Mapped[type]` for ALL column type hints
    - Use `mapped_column()` instead of `Column()`
    - Example: name: Mapped[str] = mapped_column(String(100), nullable=False)
    - ❌ FORBIDDEN: name = Column(String(100), nullable=False)

    ⭐ CROSS-PLATFORM REQUIREMENTS (CRITICAL):
    When generating main.py or server.py with Flask-SocketIO:
    - Use async_mode='threading' (NOT 'gevent' or 'eventlet')
    - Example: socketio = SocketIO(app, async_mode='threading', ...)
    - This ensures the application works on Windows, Mac, and Linux without C compilation
    - Do NOT use gevent or eventlet as they require C compilation on some platforms

    ABSOLUTE FILE GENERATION REQUIREMENT:
    - Generate EVERY SINGLE file listed in the structure above unless explicitly marked OPTIONAL
    - This includes ALL JavaScript files (.js) - they are NOT optional suggestions
    - JavaScript files MUST be separate .js files in their own files, NOT inline <script> tags
    - Missing ANY required file (especially static/js/app.js) is a CRITICAL FAILURE
    - The structure template above is NOT a suggestion - it is a MANDATORY specification
    - If a file is marked REQUIRED or listed without OPTIONAL, you MUST generate it

    MANDATORY VERIFICATION CHECKLIST - COMPLETE BEFORE FINISHING:
    ✓ Did I generate static/js/app.js as a separate .js file? (REQUIRED for web_app)
    ✓ Did I generate ALL files marked as REQUIRED in the template?
    ✓ Are all JavaScript files separate .js files (not inline scripts)?
    ✓ Did I implement complete functionality in each file (not placeholders)?

    Generate complete, production-ready code for BOTH backend AND frontend layers.
    Each file must be fully implemented with all specified functions/classes.
    NO PLACEHOLDERS. NO INCOMPLETE FILES. FULL IMPLEMENTATION ONLY.
    """
        else:
            common_footer = f"""
    CRITICAL REQUIREMENTS:
    - Ensure compatibility with existing models and schemas shown above
    - Use the same programming language ({detected_language}) and coding style
    - Import and use the existing components correctly (from models import ...)
    - Maintain consistent naming conventions
    - Follow the project structure specified above EXACTLY
    - Do NOT regenerate foundation layer files
    - Generate __init__.py files for all directories (Python projects)

    ⭐ SQLAlchemy MODEL STYLE (MANDATORY):
    If generating ANY SQLAlchemy models, you MUST use SQLAlchemy 2.0 style:
    - Use `Mapped[type]` for ALL column type hints
    - Use `mapped_column()` instead of `Column()`
    - Example: name: Mapped[str] = mapped_column(String(100), nullable=False)
    - ❌ FORBIDDEN: name = Column(String(100), nullable=False)

    ⭐ CROSS-PLATFORM REQUIREMENTS (CRITICAL):
    When generating main.py with Flask-SocketIO or similar:
    - Use async_mode='threading' (NOT 'gevent' or 'eventlet')
    - This ensures the application works on Windows, Mac, and Linux without C compilation

    Generate complete, production-ready code for the application layer only.
    Each file must be fully implemented with all specified functions/classes.
    """
        
        return common_header + specific_instructions + common_footer

    def _get_phase_required_files(self, phase: int, goal: str) -> List[str]:
        """
        Phaseごとの必須ファイルを返す
        
        Args:
            phase: Phase番号(1, 2, 3)
            goal: 実装目標
            
        Returns:
            そのPhaseで生成すべきファイルのリスト
        """
        goal_lower = goal.lower()
        
        # REST API パターンの場合
        if any(kw in goal_lower for kw in ['rest api', 'api', 'restful', 'endpoint']):
            if phase == 1:
                # Phase 1: 基盤層
                return [
                    'models/__init__.py',
                    'models/user.py',
                    'schemas/__init__.py',
                    'schemas/user.py',
                    'config.py',
                    'database.py'
                ]
            elif phase == 2:
                # Phase 2: アプリケーション層
                return [
                    'main.py',
                    'routers/__init__.py',
                    'routers/auth.py',
                    'dependencies/__init__.py',
                    'dependencies/auth.py',
                    'services/__init__.py',
                    'services/auth_service.py'
                ]
            elif phase == 3:
                # Phase 3: 環境設定
                return [
                    '.env',
                    'README.md'
                ]
        
        # CLIツールパターンの場合
        elif any(kw in goal_lower for kw in ['cli', 'command line', 'terminal']):
            if phase == 1:
                return [
                    'config.py',
                    'utils/__init__.py'
                ]
            elif phase == 2:
                return [
                    'main.py',
                    'commands/__init__.py'
                ]
            elif phase == 3:
                return [
                    'README.md'
                ]
        
        # データ処理パターンの場合
        elif any(kw in goal_lower for kw in ['data processing', 'etl', 'pipeline']):
            if phase == 1:
                return [
                    'models/__init__.py',
                    'config.py'
                ]
            elif phase == 2:
                return [
                    'main.py',
                    'processors/__init__.py'
                ]
            elif phase == 3:
                return [
                    '.env',
                    'README.md'
                ]
        
        # Web App パターンの場合 (Flask, Django, JavaScript frontend等)
        elif any(kw in goal_lower for kw in [
            'flask', 'django', 'web app', 'webapp', 'website',
            'frontend', 'html', 'javascript frontend', 'vanilla javascript',
            'full stack', 'fullstack', 'web application'
        ]):
            if phase == 1:
                # Phase 1: 基盤層
                return [
                    'config/',      # config.py or config/
                    'models/',      # models/ directory
                    'database.py'
                ]
            elif phase == 2:
                # Phase 2: アプリケーション層
                return [
                    'app.py',       # or main.py
                    'static/',      # static files directory
                ]
            elif phase == 3:
                # Phase 3: 環境設定
                return [
                    '.env',         # or .env.example
                    'README.md'
                ]
        
        # デフォルト(汎用パターン) - 最低限README.mdは必須
        if phase == 3:
            return ['README.md']
        return []

    def _validate_phase_requirements(
        self,
        phase: int,
        goal: str,
        phase_files: Dict[str, str],
        all_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Phase生成後に即座に要件検証と自動補完を実行
        
        各Phaseで生成されたファイルが要件を満たしているかを検証し、
        不足がある場合は即座に自動補完を実行します。
        
        Args:
            phase: Phase番号(1, 2, 3)
            goal: 元の実装目標
            phase_files: 当該Phaseで生成されたファイル
            all_files: これまでに生成された全ファイル
            
        Returns:
            Dict[str, str]: 補完後の全ファイル
            
        Example:
            >>> all_files = self._validate_phase_requirements(
            ...     phase=1,
            ...     goal="create a REST API",
            ...     phase_files={'models/user.py': '...'},
            ...     all_files={'models/user.py': '...'}
            ... )
        """
        logger.debug(f"[Phase {phase}] Validating requirements...")
        
        # Phase固有の必須ファイルリストを取得
        required_files = self._get_phase_required_files(phase, goal)
        
        if not required_files:
            logger.debug(f"[Phase {phase}] No specific requirements")
            return all_files
        
        # 不足ファイルを検出
        # ⚠️ CRITICAL: phase_filesだけでなく、all_filesも確認する
        # Phase 1/2で生成されたファイルがPhase 3の要件を満たす場合がある
        all_generated_filenames = set(all_files.keys())
        phase_generated_filenames = set(phase_files.keys())
        combined_filenames = all_generated_filenames | phase_generated_filenames
        
        missing = []
        
        for required in required_files:
            # ========================================
            # 特殊ケース: .env / .env.example の柔軟マッチング
            # ========================================
            if required == '.env':
                # .env または .env.example のどちらかがあればOK
                if '.env' in combined_filenames or '.env.example' in combined_filenames:
                    logger.debug(f"[Phase {phase}] .env requirement satisfied (found .env or .env.example)")
                    continue
                missing.append(required)
                continue
            
            # ========================================
            # 通常ケース: パターンマッチング
            # ========================================
            if '/' in required and not required.endswith('/'):
                # 完全一致（例: 'models/user.py'）
                if required not in combined_filenames:
                    missing.append(required)
            else:
                # ディレクトリパターン（例: 'models/' は 'models/user.py' にマッチ）
                pattern = required.rstrip('/')
                # ファイル名自体がパターンに一致（例: 'app.py'）
                if not pattern.endswith('/') and '/' not in pattern:
                    # 単一ファイル名の場合（例: 'app.py', 'README.md'）
                    if pattern in combined_filenames:
                        continue
                    # main.py / app.py の柔軟マッチング
                    if pattern == 'app.py' and 'main.py' in combined_filenames:
                        logger.debug(f"[Phase {phase}] app.py requirement satisfied by main.py")
                        continue
                    if pattern == 'main.py' and 'app.py' in combined_filenames:
                        logger.debug(f"[Phase {phase}] main.py requirement satisfied by app.py")
                        continue
                
                # ディレクトリパターンマッチング
                matched = any(f.startswith(pattern + '/') or f == pattern for f in combined_filenames)
                if not matched:
                    missing.append(required)
        
        if not missing:
            logger.debug(f"[Phase {phase}] All requirements satisfied (goal-based)")
            # ⭐ 改善7: missingが空でも_validate_import_dependenciesを実行
            # 理由: goal-basedの予測ではHTML内のCSS/JS参照を検証しない
            # _validate_import_dependencies（改善2: _validate_html_resourcesを含む）は
            # 生成されたHTML内のCSS/JS参照を検証するため、常に実行する必要がある
            logger.debug(f"[Phase {phase}] Running _validate_import_dependencies for HTML/CSS/JS validation...")
            # ⭐ 修正: post_validatorを通じて呼ぶ（_validate_import_dependenciesはPostGenerationValidatorのメソッド）
            import_requirements = self.post_validator._validate_import_dependencies(all_files)
            
            if import_requirements:
                logger.debug(f"[Phase {phase}] Found {len(import_requirements)} missing imports/resources:")
                for req in import_requirements:
                    logger.debug(f"[Phase {phase}]   - {req.filepath} ({req.file_type}): {req.reason}")
                
                # Import/Resource不足がある場合は自動補完を実行
                validation_result = {
                    'missing_files': [req.filepath for req in import_requirements],
                    'unresolved_imports': []
                }
                
                logger.debug(f"[Phase {phase}] Auto-completing {len(import_requirements)} missing files...")
                completed_files = self._auto_complete_with_import_resolution(
                    goal=goal,
                    existing_files=all_files,
                    validation_result=validation_result,
                    complexity="medium"
                )
                
                new_files_count = len(completed_files) - len(all_files)
                if new_files_count > 0:
                    logger.debug(f"[Phase {phase}] Added {new_files_count} files via HTML/CSS/JS validation")
                
                return completed_files
            
            return all_files
        
        logger.debug(f"[Phase {phase}] Missing {len(missing)} components: {missing}")
        
        # 強化されたValidation実行(Import解析含む)
        validation_result = self.requirement_validator.validate_requirements_enhanced(
            goal=goal,
            generated_files=all_files
        )
        
        logger.debug(f"[Phase {phase}] Fulfillment: {validation_result.get('fulfillment_score', 0):.2f}, Import Resolution: {validation_result.get('import_resolution_score', 1.0):.2f}")
        
        # 自動補完実行
        logger.debug(f"[Phase {phase}] Auto-completing requirements...")
        
        completed_files = self._auto_complete_with_import_resolution(
            goal=goal,
            existing_files=all_files,
            validation_result=validation_result,
            complexity="medium"
        )
        
        new_files_count = len(completed_files) - len(all_files)
        if new_files_count > 0:
            logger.verbose(f"[Phase {phase}] Added {new_files_count} files")
        
        return completed_files

    def _execute_multi_stage_generation(
        self, 
        goal: str, 
        complexity: str
    ) -> Tuple[Dict[str, str], Optional[Dict[str, Any]], Dict[str, Any]]:
        """
        多段階生成フロー(StepHUD使用 - 9ステップ)
        
        複雑なプロジェクトを3つのPhaseに分割して生成。
        各Phase内で2ステップ（Assess + Generate）を実行。
        
        Phase 1: 基盤層(models, schemas, config, database)
        Phase 2: アプリケーション層(main, routers, dependencies, services)
        Phase 3: 環境設定(.env, README)
        
        合計9ステップ:
        1-2: Foundation Layer (Assess + Generate)
        3-4: Application Layer (Assess + Generate)
        5-6: Environment Setup (Assess + Generate)
        7-9: Post-processing (Auto-completion, Lint, Quality)
        
        Args:
            goal: 実装目標
            complexity: 複雑度("complex"など)
            
        Returns:
            Tuple[Dict[str, str], Optional[Dict[str, Any]], Dict[str, Any]]:
                - all_files: 生成された全ファイル
                - lint_result: Lint結果（Linter利用不可時はNone）
                - quality_scores: 品質スコア
        """
        all_files = {}

        # ⭐ デバッグログ追加: Multi-stage generation開始
        logger.debug(f"[Multi-stage] ========================================")
        logger.debug(f"[Multi-stage] ▶▶▶ STARTING MULTI-STAGE GENERATION ▶▶▶")
        logger.debug(f"[Multi-stage] ========================================")
        logger.debug(f"[Multi-stage]   Goal length: {len(goal)} chars")
        logger.debug(f"[Multi-stage]   Complexity: {complexity}")
        logger.debug(f"[Multi-stage]   Goal preview: {goal[:200]}...")

        # StepHUD初期化（9ステップ）
        steps = [
            "Foundation Layer: Assess goal complexity",
            "Foundation Layer: Generate code implementation",
            "Application Layer: Assess goal complexity",
            "Application Layer: Generate code implementation",
            "Environment Setup: Assess goal complexity",
            "Environment Setup: Generate code implementation",
            "Auto-completion",
            "Lint & Auto-fix",
            "Quality assessment & Comprehensive Review",
        ]
        hud = StepHUD(section_title="Multi-Stage Generation", steps=steps)
        hud.start()

        try:
            # ⭐ デバッグログ追加: tryブロック開始
            logger.debug(f"[Multi-stage] Entered try block successfully")
            
            # ==========================================
            # Phase 1: 基盤層生成
            # ==========================================
            
            # Step 1: Assess goal complexity
            hud.mark("Foundation Layer: Assess goal complexity")
            
            phase1_goal = f"""Generate foundation components for: {goal}

    Focus ONLY on:
    - Data models (models/)
    - Schemas (schemas/)
    - Configuration (config/)
    - Database setup (database.py)
    - Dependencies file (requirements.txt or package.json)

    DO NOT generate:
    - main.py or application entry point
    - routers or endpoints
    - services/ (generated in Phase 2)
    - dependencies/ (generated in Phase 2)
    - .env files
    - README.md

    ⭐ SQLAlchemy MODEL STYLE (MANDATORY):
    ALL SQLAlchemy models MUST use SQLAlchemy 2.0 style with type annotations:
    - Use `Mapped[type]` for ALL column type hints
    - Use `mapped_column()` instead of `Column()`
    - Import from: `from sqlalchemy.orm import Mapped, mapped_column`
    
    ✅ CORRECT (SQLAlchemy 2.0 style - USE THIS):
        from sqlalchemy.orm import Mapped, mapped_column
        from sqlalchemy import String, Integer, Float, Boolean
        
        class User(db.Model):
            __tablename__ = 'users'
            id: Mapped[int] = mapped_column(primary_key=True)
            name: Mapped[str] = mapped_column(String(100), nullable=False)
            email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
            health: Mapped[int] = mapped_column(Integer, nullable=False)
            max_health: Mapped[int] = mapped_column(Integer, nullable=False)
            speed: Mapped[float] = mapped_column(Float, default=1.0)
            is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    
    ❌ FORBIDDEN (Legacy style - DO NOT USE):
        name = Column(String(100), nullable=False)  # NO! Use Mapped[] + mapped_column()

    ⭐ CROSS-PLATFORM REQUIREMENTS (CRITICAL):
    When generating requirements.txt:
    - Must work on Windows, Mac, and Linux without C compilation
    - Avoid packages that require C compilation (e.g., gevent, uvloop, psycopg2, psycopg2-binary)
    - Use SQLite as the default database (no additional drivers needed)
    - Do NOT include psycopg2 or psycopg2-binary in requirements.txt (they require PostgreSQL to be installed or may lack wheels for newer Python versions)
    - Do NOT include gevent or gevent-websocket in requirements.txt
    - Use eventlet or threading instead of gevent for development
    - All packages must be installable with just `pip install -r requirements.txt` on a fresh system
    
    ⭐ PYTHON 3.13 COMPATIBILITY (CRITICAL):
    Use these MINIMUM versions for Python 3.13 compatibility:
    - SQLAlchemy>=2.0.36 (NOT 2.0.25 or earlier - they fail on Python 3.13)
    - Flask-SQLAlchemy>=3.1.1
    - marshmallow>=3.21.0
    Example requirements.txt:
        Flask==3.0.0
        Flask-SQLAlchemy==3.1.1
        SQLAlchemy==2.0.36
        marshmallow==3.21.0

    Generate complete, production-ready foundation code."""
            
            logger.debug(f"[Phase 1 Goal] Content:\n{phase1_goal}")
            
            phase1_complexity = self._assess_goal_complexity(phase1_goal)
            hud.complete("Foundation Layer: Assess goal complexity")
            
            # Step 2: Generate code implementation
            hud.mark("Foundation Layer: Generate code implementation")
            
            # ⭐ デバッグログ追加: Phase 1 LLM呼び出し前
            logger.debug(f"[Phase 1] ▶ Starting _generate_code_implementation...")
            logger.debug(f"[Phase 1]   phase1_complexity: {phase1_complexity}")
            logger.debug(f"[Phase 1]   skip_validation: True, skip_multi_stage: True")
            
            try:
                phase1_files = self._generate_code_implementation(
                    goal=phase1_goal,
                    analysis="Foundation layer analysis",
                    plan="Foundation layer plan",
                    complexity=phase1_complexity,
                    skip_validation=True,
                    skip_multi_stage=True
                )
                
                # ⭐ デバッグログ追加: Phase 1 LLM呼び出し後
                logger.debug(f"[Phase 1] ◀ _generate_code_implementation returned")
                logger.debug(f"[Phase 1]   Result type: {type(phase1_files)}")
                logger.debug(f"[Phase 1]   Result is None: {phase1_files is None}")
                logger.debug(f"[Phase 1]   Result is empty: {not phase1_files if phase1_files is not None else 'N/A'}")
                if phase1_files:
                    logger.debug(f"[Phase 1]   Generated files: {list(phase1_files.keys())}")
                    
            except Exception as phase1_error:
                # ⭐ デバッグログ追加: Phase 1で例外発生
                import traceback
                logger.error(f"[Phase 1] ❌ Exception in _generate_code_implementation: {phase1_error}")
                logger.error(f"[Phase 1]   Exception type: {type(phase1_error).__name__}")
                logger.error(f"[Phase 1]   Traceback:\n{traceback.format_exc()}")
                raise  # 再スローしてexceptブロックで捕捉
            
            if not phase1_files:
                # ⭐ デバッグログ追加: Phase 1が空を返した
                logger.warning(f"[Phase 1] ⚠ _generate_code_implementation returned empty/None")
                logger.warning(f"[Phase 1]   Falling back to single-stage generation")
                hud.finish(success=False)
                logger.debug("   ⚠ Phase 1 generation failed, falling back to single-stage")
                return self._generate_code_implementation(
                    goal=goal,
                    analysis="",
                    plan="",
                    complexity=complexity,
                    skip_validation=False,
                    skip_multi_stage=True
                )
            
            all_files.update(phase1_files)
            logger.debug(f"[Phase 1] ✓ Generated {len(phase1_files)} files: {list(phase1_files.keys())}")
            hud.complete("Foundation Layer: Generate code implementation")
            
            # ==========================================
            # Phase 2: アプリケーション層生成
            # ==========================================
            
            # ⭐ デバッグログ追加: Phase 2開始
            logger.debug(f"[Phase 2] ========================================")
            logger.debug(f"[Phase 2] ▶ STARTING PHASE 2: Application Layer")
            logger.debug(f"[Phase 2] ========================================")
            
            # Step 3: Assess goal complexity
            hud.mark("Application Layer: Assess goal complexity")
            
            # プロジェクトタイプ判定
            logger.debug(f"[Phase 2] Calling _detect_project_type...")
            try:
                project_type = self._detect_project_type(goal)
                logger.debug(f"[Phase 2]   project_type: {project_type}")
            except Exception as e:
                logger.error(f"[Phase 2] ❌ _detect_project_type failed: {e}")
                raise
            
            # Phase 1のコンテキスト作成
            logger.debug(f"[Phase 2] Calling _format_generated_files_context...")
            try:
                phase1_context = self._format_generated_files_context(all_files)
                logger.debug(f"[Phase 2]   phase1_context length: {len(phase1_context)} chars")
            except Exception as e:
                logger.error(f"[Phase 2] ❌ _format_generated_files_context failed: {e}")
                raise
            
            # 言語検出
            logger.debug(f"[Phase 2] Calling _detect_project_language...")
            try:
                detected_language = self._detect_project_language(all_files)
                logger.debug(f"[Phase 2]   detected_language: {detected_language}")
            except Exception as e:
                logger.error(f"[Phase 2] ❌ _detect_project_language failed: {e}")
                raise
            
            # タイプ別Phase 2指示生成
            logger.debug(f"[Phase 2] Calling _generate_phase2_goal...")
            try:
                phase2_goal = self._generate_phase2_goal(
                    goal=goal,
                    project_type=project_type,
                    detected_language=detected_language,
                    phase1_context=phase1_context
                )
                logger.debug(f"[Phase 2]   phase2_goal length: {len(phase2_goal)} chars")
            except Exception as e:
                logger.error(f"[Phase 2] ❌ _generate_phase2_goal failed: {e}")
                raise
            
            logger.debug(f"[Phase 2 Goal] Project type: {project_type}, Language: {detected_language}")
            logger.debug(f"[Phase 2 Goal] Content:\n{phase2_goal}")
            
            logger.debug(f"[Phase 2] Calling _assess_goal_complexity for phase2_goal...")
            try:
                phase2_complexity = self._assess_goal_complexity(phase2_goal)
                logger.debug(f"[Phase 2]   phase2_complexity: {phase2_complexity}")
            except Exception as e:
                logger.error(f"[Phase 2] ❌ _assess_goal_complexity failed: {e}")
                raise
            
            hud.complete("Application Layer: Assess goal complexity")
            
            # Step 4: Generate code implementation
            hud.mark("Application Layer: Generate code implementation")
            
            # ⭐ デバッグログ追加: Phase 2 LLM呼び出し前
            logger.debug(f"[Phase 2] ▶ Starting _generate_code_implementation...")
            logger.debug(f"[Phase 2]   phase2_complexity: {phase2_complexity}")
            logger.debug(f"[Phase 2]   all_files count before: {len(all_files)}")
            
            try:
                phase2_files = self._generate_code_implementation(
                    goal=phase2_goal,
                    analysis="Application layer analysis",
                    plan="Application layer plan",
                    complexity=phase2_complexity,
                    skip_validation=True,
                    skip_multi_stage=True
                )
                
                # ⭐ デバッグログ追加: Phase 2 LLM呼び出し後
                logger.debug(f"[Phase 2] ◀ _generate_code_implementation returned")
                logger.debug(f"[Phase 2]   Result type: {type(phase2_files)}")
                logger.debug(f"[Phase 2]   Result is None: {phase2_files is None}")
                logger.debug(f"[Phase 2]   Result is empty: {not phase2_files if phase2_files is not None else 'N/A'}")
                if phase2_files:
                    logger.debug(f"[Phase 2]   Generated files: {list(phase2_files.keys())}")
                    
            except Exception as phase2_error:
                # ⭐ デバッグログ追加: Phase 2で例外発生
                import traceback
                logger.error(f"[Phase 2] ❌ Exception in _generate_code_implementation: {phase2_error}")
                logger.error(f"[Phase 2]   Exception type: {type(phase2_error).__name__}")
                logger.error(f"[Phase 2]   Traceback:\n{traceback.format_exc()}")
                raise  # 再スローしてexceptブロックで捕捉
            
            if not phase2_files:
                # ⭐ デバッグログ追加: Phase 2が空を返した
                logger.warning(f"[Phase 2] ⚠ _generate_code_implementation returned empty/None")
                logger.warning(f"[Phase 2]   Returning Phase 1 files only: {list(all_files.keys())}")
                hud.finish(success=False)
                logger.debug("   ⚠ Phase 2 generation failed, using Phase 1 only")
                return all_files
            
            all_files.update(phase2_files)
            logger.debug(f"[Phase 2] ✓ Generated {len(phase2_files)} files: {list(phase2_files.keys())}")
            hud.complete("Application Layer: Generate code implementation")
            
            # ==========================================
            # Phase 2 必須ファイルチェック + 自動補完
            # ==========================================
            required_files = self._get_required_phase2_files(goal, project_type, all_files)
            missing_files = self._find_missing_required_files(required_files, all_files)
            
            if missing_files:
                logger.debug(f"[Phase 2] ⚠️ MISSING REQUIRED FILES DETECTED")
                logger.debug(f"[Phase 2] Missing files: {missing_files}")
                logger.debug(f"[Phase 2] Attempting to auto-complete missing files...")
                
                completed_files = self._auto_complete_phase2_files(
                    missing_files=missing_files,
                    all_files=all_files,
                    goal=goal,
                    project_type=project_type,
                    detected_language=detected_language
                )
                
                if completed_files:
                    all_files.update(completed_files)
                    logger.debug(f"[Phase 2] ✓ Auto-completed {len(completed_files)} missing files: {list(completed_files.keys())}")
                else:
                    logger.debug(f"[Phase 2] ✗ Failed to auto-complete missing files")
            else:
                logger.debug(f"[Phase 2] ✓ All required files present")
            
            # ==========================================
            # Phase 3: 環境設定生成
            # ==========================================
            
            # Step 5: Assess goal complexity
            hud.mark("Environment Setup: Assess goal complexity")
            
            # 全ファイルのコンテキスト作成
            all_files_context = self._format_generated_files_context(all_files)
            
            phase3_goal = f"""Generate environment configuration for: {goal}

    Language: {detected_language}

    Generated files summary:
    {all_files_context}

    Now create:
    - .env template with all required variables (use .env.example if appropriate)
    - README.md with comprehensive setup instructions

    CRITICAL REQUIREMENTS:
    - Ensure .env includes all configuration needed by the application
    - Use sensible default values or clear placeholders (e.g., YOUR_API_KEY_HERE)
    - Document each variable's purpose in comments
    - Do NOT regenerate application or foundation layer files

    README.md MANDATORY REQUIREMENTS (⭐ CRITICAL - Users need this to run the application):
    
    ⚠️ MARKDOWN FORMATTING IS REQUIRED - The README must be properly formatted:
    - Use ## for main section headers (e.g., ## Quick Start, ## Installation)
    - Use ### for subsection headers (e.g., ### Prerequisites, ### Step 1)
    - Use numbered lists (1. 2. 3.) for sequential steps
    - Use bullet points (-) for non-sequential lists
    - Use fenced code blocks (```) for ALL of the following:
      * Shell commands: ```bash
      * Directory tree structures: ```
      * JSON examples and API responses: ```json
      * Any multi-line formatted text that requires preserved line breaks
    - WITHOUT code blocks, line breaks are IGNORED and text collapses into one line!
    
    EXAMPLES OF CORRECT CODE BLOCK USAGE:
    
    Directory structure (MUST use code block):
    ```
    project/
    ├── src/
    │   └── main.py
    └── README.md
    ```
    
    JSON/API response (MUST use code block):
    ```json
    {{"status": "success", "data": [...]}}
    ```
    
    README.md MUST include these sections with EXACT formatting:

    ## Quick Start
    
    ### Prerequisites
    - Python 3.10+ (or appropriate version)
    - pip (Python package manager)
    
    ### Installation
    
    1. **Create virtual environment**
       ```bash
       python -m venv venv
       ```
    
    2. **Activate virtual environment**
       ```bash
       # Windows (PowerShell)
       .\\venv\\Scripts\\Activate.ps1
       
       # Windows (Command Prompt)
       venv\\Scripts\\activate.bat
       
       # Linux/Mac
       source venv/bin/activate
       ```
    
    3. **Install dependencies**
       ```bash
       pip install -r requirements.txt
       ```
    
    4. **Configure environment**
       ```bash
       cp .env.example .env
       ```
    
    5. **Run the application**
       ```bash
       python main.py
       ```
    
    6. **Access the application**
       - Open browser: http://127.0.0.1:5000

    The README MUST be user-friendly and allow a new developer to run the application 
    within 2 minutes just by following the instructions. Proper Markdown formatting
    ensures readability when rendered on GitHub or other platforms.

    Generate complete, production-ready configuration files only."""
            
            logger.debug(f"[Phase 3 Goal] Content:\n{phase3_goal}")
            
            phase3_complexity = self._assess_goal_complexity(phase3_goal)
            hud.complete("Environment Setup: Assess goal complexity")
            
            # Step 6: Generate code implementation
            hud.mark("Environment Setup: Generate code implementation")
            
            # ⭐ デバッグログ追加: Phase 3 LLM呼び出し前
            logger.debug(f"[Phase 3] ▶ Starting _generate_code_implementation...")
            logger.debug(f"[Phase 3]   phase3_complexity: {phase3_complexity}")
            logger.debug(f"[Phase 3]   all_files count before: {len(all_files)}")
            
            try:
                phase3_files = self._generate_code_implementation(
                    goal=phase3_goal,
                    analysis="Environment setup analysis",
                    plan="Environment setup plan",
                    complexity=phase3_complexity,
                    skip_validation=True,
                    skip_multi_stage=True
                )
                
                # ⭐ デバッグログ追加: Phase 3 LLM呼び出し後
                logger.debug(f"[Phase 3] ◀ _generate_code_implementation returned")
                logger.debug(f"[Phase 3]   Result type: {type(phase3_files)}")
                logger.debug(f"[Phase 3]   Result is None: {phase3_files is None}")
                logger.debug(f"[Phase 3]   Result is empty: {not phase3_files if phase3_files is not None else 'N/A'}")
                if phase3_files:
                    logger.debug(f"[Phase 3]   Generated files: {list(phase3_files.keys())}")
                    
            except Exception as phase3_error:
                # ⭐ デバッグログ追加: Phase 3で例外発生
                import traceback
                logger.error(f"[Phase 3] ❌ Exception in _generate_code_implementation: {phase3_error}")
                logger.error(f"[Phase 3]   Exception type: {type(phase3_error).__name__}")
                logger.error(f"[Phase 3]   Traceback:\n{traceback.format_exc()}")
                raise  # 再スローしてexceptブロックで捕捉
            
            if not phase3_files:
                # ⭐ デバッグログ追加: Phase 3が空を返した
                logger.warning(f"[Phase 3] ⚠ _generate_code_implementation returned empty/None")
                logger.warning(f"[Phase 3]   Returning Phase 1+2 files: {list(all_files.keys())}")
                hud.finish(success=False)
                logger.debug("   ⚠ Phase 3 generation failed, using Phase 1+2 only")
                return all_files
            
            all_files.update(phase3_files)
            logger.debug(f"[Phase 3] ✓ Generated {len(phase3_files)} files: {list(phase3_files.keys())}")
            hud.complete("Environment Setup: Generate code implementation")
            
            # ==========================================
            # Post-Processing Steps
            # ==========================================
            
            # Step 7: Auto-completion
            hud.mark("Auto-completion")
            all_files = self._validate_phase_requirements(
                phase=3,
                goal=goal,
                phase_files=phase3_files,
                all_files=all_files
            )
            
            # ==========================================
            # Step 7.3: HTML-CSS/JS 整合性検証（G-1〜G-31）
            # ==========================================
            # HTMLで使用されているがCSSに未定義のクラス、
            # JSで参照されているがHTMLに存在しないIDを検出・自動補完
            # Note: Auto-completion [DONE]はG-チェック完了後に表示
            
            # G-1: HTML-CSS クラス整合性
            missing_css_classes = self._validate_html_css_class_consistency(all_files)
            if missing_css_classes:
                logger.debug(f"[G-1] Detected {len(missing_css_classes)} missing CSS classes")
                logger.debug(f"\n● CSS consistency check: {len(missing_css_classes)} missing class(es)")
                all_files = self._auto_complete_missing_css_classes(missing_css_classes, all_files)
            
            # G-2: HTML-JS ID整合性
            missing_html_ids = self._validate_html_js_id_consistency(all_files)
            if missing_html_ids:
                logger.debug(f"[G-2] Detected {len(missing_html_ids)} missing HTML IDs")
                logger.debug(f"● HTML-JS consistency check: {len(missing_html_ids)} missing ID(s)")
                all_files = self._auto_complete_missing_html_ids(missing_html_ids, all_files)
            
            # G-3: Python環境-パッケージ互換性
            compatibility_issues = self._validate_python_package_compatibility(all_files)
            if compatibility_issues:
                logger.debug(f"[G-3] Detected {len(compatibility_issues)} compatibility issues")
                logger.debug(f"● Package compatibility check: {len(compatibility_issues)} issue(s)")
                for issue in compatibility_issues:
                    logger.debug(f"  - {issue['package']}=={issue['current_version']} → {issue['fix']}")
                all_files = self._auto_fix_package_compatibility(compatibility_issues, all_files)
            
            # G-4: Pythonシンボルレベルimport整合性
            symbol_issues = self._validate_python_symbol_imports(all_files)
            if symbol_issues:
                logger.debug(f"[G-4] Detected {len(symbol_issues)} symbol import issues")
                logger.debug(f"● Symbol import check: {len(symbol_issues)} issue(s)")
                for issue in symbol_issues[:5]:  # 最大5件表示
                    logger.debug(f"  - {issue['importer']}: '{issue['symbol']}' not in {issue['module']}")
                all_files = self._auto_fix_symbol_imports(symbol_issues, all_files)
            
            # G-5: Import vs Requirements.txt 整合性チェック
            requirements_issues = self._validate_import_requirements_consistency(all_files)
            if requirements_issues:
                logger.debug(f"[G-5] Detected {len(requirements_issues)} missing packages in requirements.txt")
                logger.debug(f"● Requirements.txt check: {len(requirements_issues)} missing package(s)")
                for pkg in requirements_issues[:5]:
                    logger.debug(f"  - {pkg['import_name']} → {pkg['package_name']}")
                all_files = self._auto_fix_requirements_txt(requirements_issues, all_files)
            
            # G-6: ライブラリ要求パターンチェック
            library_issues = self._validate_library_requirements(all_files)
            if library_issues:
                logger.debug(f"[G-6] Detected {len(library_issues)} library requirement issues")
                logger.debug(f"● Library requirements check: {len(library_issues)} issue(s)")
                for issue in library_issues[:5]:
                    logger.debug(f"  - [{issue['library']}] {issue['requirement']}")
                all_files = self._auto_fix_library_requirements(library_issues, all_files)
            
            # G-7: フロントエンド依存整合性チェック
            frontend_issues = self._validate_frontend_dependencies(all_files)
            if frontend_issues:
                logger.debug(f"[G-7] Detected {len(frontend_issues)} frontend dependency issues")
                logger.debug(f"● Frontend dependencies check: {len(frontend_issues)} issue(s)")
                for issue in frontend_issues[:5]:
                    logger.debug(f"  - [{issue['type']}] {issue['dependency']}")
                all_files = self._auto_fix_frontend_dependencies(frontend_issues, all_files)
            
            # G-8: Model↔Schema整合性チェック
            schema_issues = self._validate_model_schema_consistency(all_files)
            if schema_issues:
                logger.debug(f"[G-8] Detected {len(schema_issues)} model-schema consistency issues")
                logger.debug(f"● Model-Schema consistency check: {len(schema_issues)} issue(s)")
                for issue in schema_issues[:5]:
                    logger.debug(f"  - {issue['schema']}.{issue['field']} not in {issue['model']}")
                all_files = self._auto_fix_model_schema_consistency(schema_issues, all_files)
            
            # G-9: ドキュメント環境依存チェック
            doc_issues = self._validate_document_environment_commands(all_files)
            if doc_issues:
                logger.debug(f"[G-9] Detected {len(doc_issues)} environment-dependent commands")
                logger.debug(f"● Document environment check: {len(doc_issues)} issue(s)")
                for issue in doc_issues[:5]:
                    logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                all_files = self._auto_fix_document_environment_commands(doc_issues, all_files)
            
            # G-10: クラス定義安全性チェック（再検証ループ付き）
            MAX_G10_FIX_ATTEMPTS = 2
            class_safety_issues = []
            for g10_attempt in range(MAX_G10_FIX_ATTEMPTS):
                class_safety_issues = self._validate_class_definition_safety(all_files)
                if not class_safety_issues:
                    if g10_attempt > 0:
                        logger.debug(f"[G-10] ✅ All issues fixed after {g10_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-10] Detected {len(class_safety_issues)} issues (attempt {g10_attempt + 1}/{MAX_G10_FIX_ATTEMPTS})")
                if g10_attempt == 0:
                    logger.debug(f"● Class definition safety check: {len(class_safety_issues)} issue(s)")
                    for issue in class_safety_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                
                all_files = self._auto_fix_class_definition_safety(class_safety_issues, all_files)
            
            if class_safety_issues:
                logger.warning(f"[G-10] ⚠ {len(class_safety_issues)} issues remain after {MAX_G10_FIX_ATTEMPTS} fix attempts")
            
            # G-11: フレームワーク予約語チェック（再検証ループ付き）
            MAX_G11_FIX_ATTEMPTS = 2
            framework_reserved_issues = []
            for g11_attempt in range(MAX_G11_FIX_ATTEMPTS):
                framework_reserved_issues = self._validate_framework_reserved_words(all_files)
                if not framework_reserved_issues:
                    if g11_attempt > 0:
                        logger.debug(f"[G-11] ✅ All issues fixed after {g11_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-11] Detected {len(framework_reserved_issues)} issues (attempt {g11_attempt + 1}/{MAX_G11_FIX_ATTEMPTS})")
                if g11_attempt == 0:
                    logger.debug(f"● Framework reserved word check: {len(framework_reserved_issues)} issue(s)")
                    for issue in framework_reserved_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['reserved_word']}")
                
                all_files = self._auto_fix_framework_reserved_words(framework_reserved_issues, all_files)
            
            if framework_reserved_issues:
                logger.warning(f"[G-11] ⚠ {len(framework_reserved_issues)} issues remain after {MAX_G11_FIX_ATTEMPTS} fix attempts")
            
            # G-12: Python組み込み名シャドウイングチェック（再検証ループ付き）
            MAX_G12_FIX_ATTEMPTS = 2
            builtin_shadowing_issues = []
            for g12_attempt in range(MAX_G12_FIX_ATTEMPTS):
                builtin_shadowing_issues = self._validate_python_builtin_shadowing(all_files)
                if not builtin_shadowing_issues:
                    if g12_attempt > 0:
                        logger.debug(f"[G-12] ✅ All issues fixed after {g12_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-12] Detected {len(builtin_shadowing_issues)} issues (attempt {g12_attempt + 1}/{MAX_G12_FIX_ATTEMPTS})")
                if g12_attempt == 0:
                    logger.debug(f"● Python builtin shadowing check: {len(builtin_shadowing_issues)} issue(s)")
                    for issue in builtin_shadowing_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['builtin_name']}")
                
                all_files = self._auto_fix_python_builtin_shadowing(builtin_shadowing_issues, all_files)
            
            if builtin_shadowing_issues:
                logger.warning(f"[G-12] ⚠ {len(builtin_shadowing_issues)} issues remain after {MAX_G12_FIX_ATTEMPTS} fix attempts")
            
            # G-13: Flask Blueprintルート重複チェック（再検証ループ付き）
            MAX_G13_FIX_ATTEMPTS = 2
            route_duplicate_issues = []
            for g13_attempt in range(MAX_G13_FIX_ATTEMPTS):
                route_duplicate_issues = self._validate_flask_route_duplicates(all_files)
                if not route_duplicate_issues:
                    if g13_attempt > 0:
                        logger.debug(f"[G-13] ✅ All issues fixed after {g13_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-13] Detected {len(route_duplicate_issues)} issues (attempt {g13_attempt + 1}/{MAX_G13_FIX_ATTEMPTS})")
                if g13_attempt == 0:
                    logger.debug(f"● Flask route duplicate check: {len(route_duplicate_issues)} issue(s)")
                    for issue in route_duplicate_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['method']} {issue['route']}")
                
                all_files = self._auto_fix_flask_route_duplicates(route_duplicate_issues, all_files)
            
            if route_duplicate_issues:
                logger.warning(f"[G-13] ⚠ {len(route_duplicate_issues)} issues remain after {MAX_G13_FIX_ATTEMPTS} fix attempts")
            
            # G-14: SQLAlchemyテーブル名重複チェック（再検証ループ付き）
            MAX_G14_FIX_ATTEMPTS = 2
            table_duplicate_issues = []
            for g14_attempt in range(MAX_G14_FIX_ATTEMPTS):
                table_duplicate_issues = self._validate_sqlalchemy_table_duplicates(all_files)
                if not table_duplicate_issues:
                    if g14_attempt > 0:
                        logger.debug(f"[G-14] ✅ All issues fixed after {g14_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-14] Detected {len(table_duplicate_issues)} issues (attempt {g14_attempt + 1}/{MAX_G14_FIX_ATTEMPTS})")
                if g14_attempt == 0:
                    logger.debug(f"● SQLAlchemy table duplicate check: {len(table_duplicate_issues)} issue(s)")
                    for issue in table_duplicate_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - table '{issue['table']}'")
                
                all_files = self._auto_fix_sqlalchemy_table_duplicates(table_duplicate_issues, all_files)
            
            if table_duplicate_issues:
                logger.warning(f"[G-14] ⚠ {len(table_duplicate_issues)} issues remain after {MAX_G14_FIX_ATTEMPTS} fix attempts")
            
            # G-15: JavaScript予約語チェック（再検証ループ付き）
            MAX_G15_FIX_ATTEMPTS = 2
            js_reserved_issues = []
            for g15_attempt in range(MAX_G15_FIX_ATTEMPTS):
                js_reserved_issues = self._validate_javascript_reserved_words(all_files)
                if not js_reserved_issues:
                    if g15_attempt > 0:
                        logger.debug(f"[G-15] ✅ All issues fixed after {g15_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-15] Detected {len(js_reserved_issues)} issues (attempt {g15_attempt + 1}/{MAX_G15_FIX_ATTEMPTS})")
                if g15_attempt == 0:
                    logger.debug(f"● JavaScript reserved word check: {len(js_reserved_issues)} issue(s)")
                    for issue in js_reserved_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['reserved_word']}")
                
                all_files = self._auto_fix_javascript_reserved_words(js_reserved_issues, all_files)
            
            if js_reserved_issues:
                logger.warning(f"[G-15] ⚠ {len(js_reserved_issues)} issues remain after {MAX_G15_FIX_ATTEMPTS} fix attempts")
            
            # G-16: SQLAlchemy relationship参照先検証（再検証ループ付き）
            MAX_G16_FIX_ATTEMPTS = 2
            relationship_issues = []
            for g16_attempt in range(MAX_G16_FIX_ATTEMPTS):
                relationship_issues = self._validate_relationship_references(all_files)
                if not relationship_issues:
                    if g16_attempt > 0:
                        logger.debug(f"[G-16] ✅ All issues fixed after {g16_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-16] Detected {len(relationship_issues)} issues (attempt {g16_attempt + 1}/{MAX_G16_FIX_ATTEMPTS})")
                if g16_attempt == 0:
                    logger.debug(f"● Relationship reference check: {len(relationship_issues)} issue(s)")
                    for issue in relationship_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['type']}")
                
                all_files = self._auto_fix_relationship_references(relationship_issues, all_files)
            
            if relationship_issues:
                logger.warning(f"[G-16] ⚠ {len(relationship_issues)} issues remain after {MAX_G16_FIX_ATTEMPTS} fix attempts")
            
            # G-17: ForeignKey参照先検証（再検証ループ付き）
            MAX_G17_FIX_ATTEMPTS = 2
            foreignkey_issues = []
            for g17_attempt in range(MAX_G17_FIX_ATTEMPTS):
                foreignkey_issues = self._validate_foreignkey_references(all_files)
                if not foreignkey_issues:
                    if g17_attempt > 0:
                        logger.debug(f"[G-17] ✅ All issues fixed after {g17_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-17] Detected {len(foreignkey_issues)} issues (attempt {g17_attempt + 1}/{MAX_G17_FIX_ATTEMPTS})")
                if g17_attempt == 0:
                    logger.debug(f"● ForeignKey reference check: {len(foreignkey_issues)} issue(s)")
                    for issue in foreignkey_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['referenced_table']}")
                
                all_files = self._auto_fix_foreignkey_references(foreignkey_issues, all_files)
            
            if foreignkey_issues:
                logger.warning(f"[G-17] ⚠ {len(foreignkey_issues)} issues remain after {MAX_G17_FIX_ATTEMPTS} fix attempts")
            
            # G-18: SQLAlchemy Enum定義検証（再検証ループ付き）
            MAX_G18_FIX_ATTEMPTS = 2
            enum_usage_issues = []
            for g18_attempt in range(MAX_G18_FIX_ATTEMPTS):
                enum_usage_issues = self._validate_sqlalchemy_enum_usage(all_files)
                if not enum_usage_issues:
                    if g18_attempt > 0:
                        logger.debug(f"[G-18] ✅ All issues fixed after {g18_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-18] Detected {len(enum_usage_issues)} issues (attempt {g18_attempt + 1}/{MAX_G18_FIX_ATTEMPTS})")
                if g18_attempt == 0:
                    logger.debug(f"● Enum usage check: {len(enum_usage_issues)} issue(s)")
                    for issue in enum_usage_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['referenced_class']}")
                
                all_files = self._auto_fix_sqlalchemy_enum_usage(enum_usage_issues, all_files)
            
            if enum_usage_issues:
                logger.warning(f"[G-18] ⚠ {len(enum_usage_issues)} issues remain after {MAX_G18_FIX_ATTEMPTS} fix attempts")
            
            # G-19: Mapped型アノテーション整合性検証（再検証ループ付き）
            MAX_G19_FIX_ATTEMPTS = 2
            mapped_type_issues = []
            for g19_attempt in range(MAX_G19_FIX_ATTEMPTS):
                mapped_type_issues = self._validate_mapped_type_consistency(all_files)
                if not mapped_type_issues:
                    if g19_attempt > 0:
                        logger.debug(f"[G-19] ✅ All issues fixed after {g19_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-19] Detected {len(mapped_type_issues)} issues (attempt {g19_attempt + 1}/{MAX_G19_FIX_ATTEMPTS})")
                if g19_attempt == 0:
                    logger.debug(f"● Mapped type consistency check: {len(mapped_type_issues)} issue(s)")
                    for issue in mapped_type_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['attribute']}")
                
                all_files = self._auto_fix_mapped_type_consistency(mapped_type_issues, all_files)
            
            if mapped_type_issues:
                logger.warning(f"[G-19] ⚠ {len(mapped_type_issues)} issues remain after {MAX_G19_FIX_ATTEMPTS} fix attempts")
            
            # G-20: Flask静的パス検証（再検証ループ付き）
            MAX_G20_FIX_ATTEMPTS = 2
            flask_static_issues = []
            for g20_attempt in range(MAX_G20_FIX_ATTEMPTS):
                flask_static_issues = self._validate_flask_static_paths(all_files)
                if not flask_static_issues:
                    if g20_attempt > 0:
                        logger.debug(f"[G-20] ✅ All issues fixed after {g20_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-20] Detected {len(flask_static_issues)} issues (attempt {g20_attempt + 1}/{MAX_G20_FIX_ATTEMPTS})")
                if g20_attempt == 0:
                    logger.debug(f"● Flask static path check: {len(flask_static_issues)} issue(s)")
                    for issue in flask_static_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['current_path']} → {issue['suggested_path']}")
                
                all_files = self._auto_fix_flask_static_paths(flask_static_issues, all_files)
            
            if flask_static_issues:
                logger.warning(f"[G-20] ⚠ {len(flask_static_issues)} issues remain after {MAX_G20_FIX_ATTEMPTS} fix attempts")
            
            # G-21: HTML参照整合性検証（再検証ループ付き）
            MAX_G21_FIX_ATTEMPTS = 2
            html_asset_issues = []
            for g21_attempt in range(MAX_G21_FIX_ATTEMPTS):
                html_asset_issues = self._validate_html_asset_references(all_files)
                if not html_asset_issues:
                    if g21_attempt > 0:
                        logger.debug(f"[G-21] ✅ All issues fixed after {g21_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-21] Detected {len(html_asset_issues)} issues (attempt {g21_attempt + 1}/{MAX_G21_FIX_ATTEMPTS})")
                if g21_attempt == 0:
                    logger.debug(f"● HTML asset reference check: {len(html_asset_issues)} issue(s)")
                    for issue in html_asset_issues[:5]:
                        logger.debug(f"  - {issue['html_file']}:{issue['line']} - {issue['ref_type']}: {issue['ref_path']}")
                
                all_files = self._auto_fix_html_asset_references(html_asset_issues, all_files)
            
            if html_asset_issues:
                logger.warning(f"[G-21] ⚠ {len(html_asset_issues)} issues remain after {MAX_G21_FIX_ATTEMPTS} fix attempts")
            
            # G-22: JavaScriptアニメーションループパターン検証（再検証ループ付き）
            MAX_G22_FIX_ATTEMPTS = 2
            js_animation_issues = []
            for g22_attempt in range(MAX_G22_FIX_ATTEMPTS):
                js_animation_issues = self._validate_js_animation_loop(all_files)
                if not js_animation_issues:
                    if g22_attempt > 0:
                        logger.debug(f"[G-22] ✅ All issues fixed after {g22_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-22] Detected {len(js_animation_issues)} issues (attempt {g22_attempt + 1}/{MAX_G22_FIX_ATTEMPTS})")
                if g22_attempt == 0:
                    logger.debug(f"● JavaScript animation loop check: {len(js_animation_issues)} issue(s)")
                    for issue in js_animation_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['method_name']}() called without timestamp")
                
                all_files = self._auto_fix_js_animation_loop(js_animation_issues, all_files)
            
            if js_animation_issues:
                logger.warning(f"[G-22] ⚠ {len(js_animation_issues)} issues remain after {MAX_G22_FIX_ATTEMPTS} fix attempts")
            
            # ESLint統合: JavaScript品質チェック
            eslint_result = None
            try:
                if self._check_eslint_available():
                    logger.debug("[ESLint] Running JavaScript linter (multi-stage)...")
                    eslint_result = self._run_eslint_on_files(all_files)
                    
                    if eslint_result['files_checked'] > 0:
                        error_count = len(eslint_result['errors'])
                        warning_count = len(eslint_result['warnings'])
                        
                        if error_count > 0 or warning_count > 0:
                            logger.debug("")
                            logger.debug(f"● ESLint: {error_count} error(s), {warning_count} warning(s)")
                            
                            for issue in eslint_result['errors'][:5]:
                                logger.debug(f"  - {issue['file']}:{issue['line']} [{issue['rule']}] {issue['message']}")
                            
                            if error_count > 5:
                                logger.debug(f"  ... and {error_count - 5} more error(s)")
                            
                            if error_count > 0:
                                logger.debug("[ESLint] Attempting auto-fix with LLM...")
                                all_files = self._fix_eslint_errors_with_llm(
                                    all_files,
                                    eslint_result['errors']
                                )
                                
                                recheck_result = self._run_eslint_on_files(all_files)
                                error_count_after = len(recheck_result['errors'])
                                warning_count_after = len(recheck_result['warnings'])
                                
                                logger.debug("")
                                logger.debug(f"  After auto-fix: {error_count_after} error(s), {warning_count_after} warning(s)")
                                
                                if error_count_after == 0:
                                    logger.debug(f"  ✓ All ESLint errors resolved!")
                                
                                eslint_result = recheck_result
                        else:
                            logger.debug("[ESLint] No issues found")
                else:
                    logger.debug("[ESLint] Not available, skipping JavaScript lint check")
                    
            except Exception as e:
                logger.debug(f"[ESLint] Error during lint check: {e}")
            
            # G-23: API契約整合性チェック（警告のみ、自動修正なし）
            api_contract_issues = self._validate_api_contract_consistency(all_files)
            if api_contract_issues:
                logger.debug(f"● API contract consistency: {len(api_contract_issues)} warning(s)")
                for issue in api_contract_issues[:3]:
                    logger.debug(f"  - {issue['details']}")
            
            # G-24: back_populates相互参照チェック
            MAX_G24_FIX_ATTEMPTS = 2
            back_populates_issues = []
            for g24_attempt in range(MAX_G24_FIX_ATTEMPTS):
                back_populates_issues = self._validate_back_populates_consistency(all_files)
                if not back_populates_issues:
                    if g24_attempt > 0:
                        logger.debug(f"[G-24] ✅ All issues fixed after {g24_attempt} attempt(s)")
                    break
                logger.debug(f"[G-24] Detected {len(back_populates_issues)} issues (attempt {g24_attempt + 1}/{MAX_G24_FIX_ATTEMPTS})")
                all_files = self._auto_fix_back_populates_consistency(back_populates_issues, all_files)
            
            if back_populates_issues:
                logger.debug(f"● back_populates consistency: {len(back_populates_issues)} error(s)")
                for issue in back_populates_issues[:3]:
                    logger.debug(f"  - {issue['details']}")
            
            # G-25: ForeignKey型注釈欠如チェック
            foreignkey_annotation_issues = self._validate_foreignkey_type_annotation(all_files)
            if foreignkey_annotation_issues:
                logger.debug(f"[G-25] Detected {len(foreignkey_annotation_issues)} issues, auto-fixing...")
                all_files = self._auto_fix_foreignkey_type_annotation(foreignkey_annotation_issues, all_files)
                foreignkey_annotation_issues = self._validate_foreignkey_type_annotation(all_files)
            
            if foreignkey_annotation_issues:
                logger.debug(f"● ForeignKey type annotations: {len(foreignkey_annotation_issues)} warning(s)")
            
            # G-26: Mapped型とSQLAlchemy型の不一致チェック
            mapped_mismatch_issues = self._validate_mapped_sqlalchemy_type_mismatch(all_files)
            if mapped_mismatch_issues:
                logger.debug(f"[G-26] Detected {len(mapped_mismatch_issues)} issues, auto-fixing...")
                all_files = self._auto_fix_mapped_sqlalchemy_type_mismatch(mapped_mismatch_issues, all_files)
                mapped_mismatch_issues = self._validate_mapped_sqlalchemy_type_mismatch(all_files)
            
            if mapped_mismatch_issues:
                logger.debug(f"● Mapped/SQLAlchemy type mismatch: {len(mapped_mismatch_issues)} warning(s)")
            
            # G-27: relationship overlaps検出
            relationship_overlap_issues = self._validate_relationship_overlaps(all_files)
            if relationship_overlap_issues:
                all_files = self._auto_fix_relationship_overlaps(relationship_overlap_issues, all_files)
                logger.debug(f"● Relationship overlaps: {len(relationship_overlap_issues)} warning(s)")
            
            # G-28: 循環インポート検出
            circular_import_issues = self._validate_circular_imports(all_files)
            if circular_import_issues:
                all_files = self._auto_fix_circular_imports(circular_import_issues, all_files)
                logger.debug(f"● Circular imports: {len(circular_import_issues)} error(s)")
                for issue in circular_import_issues[:2]:
                    logger.debug(f"  - {issue['details']}")
            
            # G-29: Marshmallow Nested循環参照検出
            marshmallow_circular_issues = self._validate_marshmallow_nested_circular(all_files)
            if marshmallow_circular_issues:
                all_files = self._auto_fix_marshmallow_nested_circular(marshmallow_circular_issues, all_files)
                logger.debug(f"● Marshmallow Nested circular: {len(marshmallow_circular_issues)} warning(s)")
            
            # G-30: JavaScript this参照喪失チェック
            js_this_loss_issues = self._validate_js_this_context_loss(all_files)
            if js_this_loss_issues:
                logger.debug(f"[G-30] Detected {len(js_this_loss_issues)} issues, auto-fixing...")
                all_files = self._auto_fix_js_this_context_loss(js_this_loss_issues, all_files)
                js_this_loss_issues = self._validate_js_this_context_loss(all_files)
            
            if js_this_loss_issues:
                logger.debug(f"● JavaScript this context loss: {len(js_this_loss_issues)} error(s)")
                for issue in js_this_loss_issues[:3]:
                    logger.debug(f"  - {issue['details']}")
            
            # G-31: Marshmallow API誤用検出（再検証ループ付き）
            MAX_G31_FIX_ATTEMPTS = 2
            marshmallow_api_issues = []
            for g31_attempt in range(MAX_G31_FIX_ATTEMPTS):
                marshmallow_api_issues = self._validate_marshmallow_api_usage(all_files)
                if not marshmallow_api_issues:
                    if g31_attempt > 0:
                        logger.debug(f"[G-31] ✅ All issues fixed after {g31_attempt} attempt(s)")
                    break
                
                logger.debug(f"[G-31] Detected {len(marshmallow_api_issues)} issues (attempt {g31_attempt + 1}/{MAX_G31_FIX_ATTEMPTS})")
                if g31_attempt == 0:
                    logger.debug(f"● Marshmallow API usage check: {len(marshmallow_api_issues)} issue(s)")
                    for issue in marshmallow_api_issues[:5]:
                        logger.debug(f"  - {issue['file']}:{issue['line']} - {issue['field']}")
                
                all_files = self._auto_fix_marshmallow_api_usage(marshmallow_api_issues, all_files)
            
            if marshmallow_api_issues:
                logger.warning(f"[G-31] ⚠ {len(marshmallow_api_issues)} issues remain after {MAX_G31_FIX_ATTEMPTS} fix attempts")
            
            if not missing_css_classes and not missing_html_ids and not compatibility_issues and not symbol_issues and not requirements_issues and not library_issues and not frontend_issues and not schema_issues and not doc_issues and not class_safety_issues and not framework_reserved_issues and not builtin_shadowing_issues and not route_duplicate_issues and not table_duplicate_issues and not js_reserved_issues and not relationship_issues and not foreignkey_issues and not enum_usage_issues and not mapped_type_issues and not flask_static_issues and not html_asset_issues and not js_animation_issues and not back_populates_issues and not circular_import_issues and not js_this_loss_issues and not marshmallow_api_issues:
                logger.debug("[G-1/.../G-31] All consistency checks passed")
            
            # HUD: Auto-completion完了（G-1〜G-31完了後）
            hud.complete("Auto-completion")
            
            # ==========================================
            # Step 7.5: HTML参照修正（フレームワーク禁止モード）
            # ==========================================
            if getattr(self, '_no_framework_mode', False):
                logger.debug("[G-32] Checking HTML script references for vanilla JS...")
                all_files = self._fix_html_script_reference_for_vanilla_js(all_files)
            
            # ==========================================
            # Step 7.6: Runtime Import Test（対策C）
            # ==========================================
            # 生成されたコードを実際にインポートして実行時エラーを検出
            # 静的解析では検出できないクラス定義時のエラーなどを事前に発見
            logger.debug("[Runtime Import] Starting import test (multi-stage)...")
            
            import_success, import_error, failing_file = self._validate_runtime_import(all_files)
            
            # ⭐ 対策A: 外部パッケージ不足の場合は情報表示のみ（LLM修正は不要）
            if import_success and import_error and import_error.startswith("EXTERNAL_PACKAGE:"):
                package_name = import_error.replace("EXTERNAL_PACKAGE:", "")
                logger.debug(f"\n● Runtime import test skipped:")
                logger.debug(f"   ℹ Requires external package: {package_name}")
                logger.debug(f"   → Run: pip install -r requirements.txt")
                logger.debug(f"[Runtime Import] Skipped due to external package: {package_name}")
            elif not import_success:
                logger.debug(f"\n⚠ Runtime import error detected:")
                logger.debug(f"   File: {failing_file or 'Unknown'}")
                logger.debug(f"   Error: {import_error}")
                
                # LLMで自動修正を試行
                MAX_IMPORT_FIX_ATTEMPTS = 2
                for attempt in range(MAX_IMPORT_FIX_ATTEMPTS):
                    logger.debug(f"[Runtime Import] Fix attempt {attempt + 1}/{MAX_IMPORT_FIX_ATTEMPTS}")
                    
                    fixed_code = self._fix_import_error_with_llm(
                        all_files,
                        import_error,
                        failing_file
                    )
                    
                    if fixed_code:
                        # 修正後に再テスト
                        retry_success, retry_error, retry_file = self._validate_runtime_import(fixed_code)
                        
                        # 外部パッケージ不足になった場合もOK
                        if retry_success:
                            if retry_error and retry_error.startswith("EXTERNAL_PACKAGE:"):
                                package_name = retry_error.replace("EXTERNAL_PACKAGE:", "")
                                logger.debug(f"   ✅ Code error fixed. External package required: {package_name}")
                            else:
                                logger.debug(f"   ✅ Import error fixed (attempt {attempt + 1})")
                            all_files = fixed_code
                            logger.debug(f"[Runtime Import] ✅ Fixed on attempt {attempt + 1}")
                            break
                        else:
                            # 修正したが別のエラーが発生
                            import_error = retry_error
                            failing_file = retry_file
                            all_files = fixed_code  # 部分的な修正を保持
                            logger.debug(f"[Runtime Import] Partial fix, new error: {retry_error}")
                    else:
                        logger.debug(f"[Runtime Import] Fix attempt {attempt + 1} failed")
                else:
                    # 全ての試行が失敗
                    logger.debug(f"   ⚠ Could not auto-fix. Manual review required.")
                    logger.debug("[Runtime Import] All fix attempts failed")
            
            # Step 8: Lint & Auto-fix
            hud.mark("Lint & Auto-fix")
            
            # 実際の処理を実行（呼び出し元から移動）
            lint_result = None
            try:
                from cognix.linter_integration import LinterIntegration
                from cognix import hud_components as hud_comp
                
                linter = LinterIntegration()
                
                # linterが利用可能な場合のみ実行
                if linter.available_linters:
                    logger.debug("[Linter] Running external linters on multi-stage generated code...")
                    lint_result = linter.lint_generated_code(all_files)
                    
                    if lint_result['has_errors']:
                        error_count = len(lint_result['errors'])
                        warning_count = len(lint_result['warnings'])
                        linters_used = ', '.join(lint_result['linters_used'])
                        
                        # ⭐ Zen HUD: Multi-stage初期Lintエラー数を記録
                        self._zen_summary["lint"]["initial"] = error_count
                        
                        logger.debug(f"[Linter] Detected {error_count} error(s), {warning_count} warning(s)")
                        logger.debug(f"[Linter] Used: {linters_used}")
                        
                        logger.debug("")
                        logger.debug(hud_comp.lint_summary_line(0, warning_count, error_count))
                        
                        if lint_result['can_auto_fix']:
                            logger.debug("[Linter] Attempting auto-fix with LLM...")
                            logger.debug("[Linter] Auto-fix attempt started")
                            
                            fixed_files = self._fix_linter_errors_with_llm(
                                all_files,
                                lint_result['errors'],
                                lint_result.get('file_languages', {})
                            )
                            
                            if fixed_files:
                                logger.debug("[Linter] Auto-fix successful")
                                logger.debug(f"{Icon.GEAR.value} Linter errors have been auto-fixed")
                                all_files = fixed_files
                                
                                try:
                                    logger.debug("[Linter] Re-running linter after auto-fix...")
                                    recheck_result = linter.lint_generated_code(all_files)
                                    
                                    error_count_before = len(lint_result.get('errors', []))
                                    warning_count_before = len(lint_result.get('warnings', []))
                                    error_count_after = len(recheck_result.get('errors', []))
                                    warning_count_after = len(recheck_result.get('warnings', []))
                                    
                                    logger.debug("")
                                    logger.debug("  After auto-fix:")
                                    logger.debug(hud_comp.lint_summary_line(0, warning_count_after, error_count_after))
                                    
                                    if error_count_after > 0 or warning_count_after > 0:
                                        breakdown = self._get_error_breakdown(
                                            recheck_result.get('errors', []),
                                            recheck_result.get('warnings', [])
                                        )
                                        error_parts = []
                                        if breakdown['critical'] > 0:
                                            error_parts.append(f"Critical={breakdown['critical']}")
                                        if breakdown['high'] > 0:
                                            error_parts.append(f"High={breakdown['high']}")
                                        if breakdown['medium'] > 0:
                                            error_parts.append(f"Medium={breakdown['medium']}")
                                        if breakdown['low'] > 0:
                                            error_parts.append(f"Low={breakdown['low']} (style issues)")
                                        if breakdown['env'] > 0:
                                            error_parts.append(f"Env={breakdown['env']}")
                                        if breakdown['warnings'] > 0:
                                            error_parts.append(f"Warnings={breakdown['warnings']}")
                                        
                                        if error_parts:
                                            logger.debug(f"  Error Types: {', '.join(error_parts)}")
                                    
                                    lint_result = recheck_result
                                    
                                    # ⭐ Zen HUD: Multi-stage Auto-fix後のLintエラー数を記録
                                    self._zen_summary["lint"]["final"] = error_count_after
                                    self._zen_summary["lint"]["fixed"] = (error_count_after < error_count_before)
                                    
                                    errors_fixed = error_count_before - error_count_after
                                    if error_count_after == 0 and warning_count_after == 0:
                                        logger.debug(f"  ✓ All linter errors resolved!")
                                    elif errors_fixed > 0:
                                        logger.debug(f"  ✓ {errors_fixed} errors fixed, {error_count_after} remaining")
                                    else:
                                        logger.debug(f"  ⚠ No errors were fixed automatically")
                                    
                                    logger.debug(f"[Linter] Re-check complete: {error_count_after} errors, {warning_count_after} warnings")
                                    
                                except Exception as e:
                                    logger.debug(f"[Linter] Re-check failed: {e}")
                            
                            else:
                                logger.debug("[Linter] Auto-fix failed")
                                logger.debug("  ⚠  Auto-fix failed. Manual review required.")
                        else:
                            logger.debug("  → run with --verbose for details")
                            logger.debug("  ⚠  Manual review required (syntax errors detected)")
                    
                    elif lint_result['has_warnings']:
                        warning_count = len(lint_result['warnings'])
                        logger.debug(f"[Linter] Detected {warning_count} warning(s)")
                        logger.debug(f"\n● Code passed linter checks ({warning_count} warnings)")
                    else:
                        logger.debug("[Linter] No issues detected")
                        logger.debug("\n● Code passed all linter checks")
                else:
                    logger.debug("[Linter] No external linters available (optional feature)")
            
            except ImportError:
                logger.debug("[Linter] LinterIntegration not available (optional feature)")
            except Exception as e:
                logger.debug(f"[Linter] Error during linting: {e}")
            
            # ==========================================
            # Cross-File Consistency Check (Multi-stage)
            # モデル定義と初期データの整合性を検証
            # ==========================================
            try:
                logger.debug("[Cross-File] Starting cross-file consistency check...")
                cross_file_result = self._check_cross_file_consistency(all_files)
                
                if cross_file_result['has_issues']:
                    issue_count = len(cross_file_result['issues'])
                    logger.debug(f"[Cross-File] Detected {issue_count} consistency issue(s)")
                    
                    # 問題の詳細をログに出力
                    for issue in cross_file_result['issues']:
                        logger.debug(
                            f"[Cross-File]   {issue['model']}.{issue['field']}: "
                            f"{issue['message']}"
                        )
                    
                    # Zen HUD: 問題検出を表示
                    logger.debug("")
                    logger.debug(f"  {Icon.WARNING.value} Cross-file consistency: {issue_count} issue(s) detected")
                    
                    # LLMによる自動修正を試行
                    logger.debug("[Cross-File] Attempting auto-fix with LLM...")
                    fixed_files = self._fix_cross_file_issues_with_llm(
                        all_files,
                        cross_file_result['issues'],
                        cross_file_result.get('model_definitions', {})
                    )
                    
                    if fixed_files:
                        # 修正されたファイルを反映
                        for filename, code in fixed_files.items():
                            all_files[filename] = code
                        
                        logger.debug(f"[Cross-File] Auto-fix successful: {list(fixed_files.keys())}")
                        logger.debug(f"  {Icon.GEAR.value} Cross-file issues have been auto-fixed")
                        
                        # 修正後に再検証
                        recheck_result = self._check_cross_file_consistency(all_files)
                        if recheck_result['has_issues']:
                            remaining = len(recheck_result['issues'])
                            logger.debug(f"  {Icon.WARNING.value} {remaining} issue(s) remain after auto-fix")
                        else:
                            logger.debug(f"  {Icon.CHECK.value} All cross-file issues resolved")
                    else:
                        logger.debug("[Cross-File] Auto-fix failed")
                        logger.debug(f"  {Icon.WARNING.value} Auto-fix failed. Manual review required.")
                else:
                    logger.debug("[Cross-File] No consistency issues detected")
            
            except Exception as e:
                logger.debug(f"[Cross-File] Error during consistency check: {e}")
                # エラーが発生しても処理を継続
            
            hud.complete("Lint & Auto-fix")
            
            # Step 9: Quality assessment & Comprehensive Review
            hud.mark("Quality assessment & Comprehensive Review")
            
            # 実際の処理を実行（呼び出し元から移動）
            quality_scores = self._assess_code_quality(
                all_files,
                complexity,
                goal,
                lint_result=lint_result
            )
            
            # Quality issues の検出と表示（Multi-stage generation）
            quality_issues = []
            quality_issue_count = 0
            
            for filename, score in quality_scores.items():
                if score < 0.70:  # 70%未満は問題あり
                    quality_issue_count += 1
                    # 詳細を取得
                    code = all_files.get(filename, '')
                    ext = filename.split('.')[-1]
                    placeholder_count, detected_lines = self._detect_placeholders_in_comments(code, ext)
                    
                    if placeholder_count > 0:
                        quality_issues.append({
                            'filename': filename,
                            'score': score,
                            'issue_type': 'placeholder',
                            'count': placeholder_count,
                            'details': detected_lines[:3]  # 最大3件
                        })
            
            # Quality issues 表示（Multi-stage generation）
            if quality_issue_count > 0:
                logger.debug(f"\n[Quality] Code quality check: {quality_issue_count} quality issue(s) detected")
                
                for issue in quality_issues[:5]:  # 最大5件表示
                    logger.debug(f"  - {issue['filename']}: {issue['count']} placeholder(s) in comments")
                    for detail in issue['details']:
                        logger.debug(f"    • {detail}")
                
                # Quality issues の自動修正
                if quality_issues:
                    logger.debug("[Quality] Attempting to fix quality issues...")
                    fixed_files = self._fix_quality_issues_with_llm(all_files, quality_issues)
                    
                    if fixed_files:
                        all_files = fixed_files
                        logger.debug("[Quality] Quality issues fixed")
                        logger.debug(f"{Icon.GEAR.value} Quality issues have been auto-fixed")
                        
                        # Quality 再評価
                        quality_scores = self._assess_code_quality(
                            all_files,
                            complexity,
                            goal,
                            lint_result=lint_result
                        )
            
            # Step 9の続き: 総合レビュー（Comprehensive Review）
            # ==========================================
            # 「一発で動く」を保証するための最終チェック
            logger.debug("[Comprehensive Review] Starting final review...")
            all_files = self._final_comprehensive_review(all_files, goal)
            
            # 🆕 Comprehensive Review後のHTML参照修正（スタブ再生成後に必要）
            if getattr(self, '_no_framework_mode', False):
                logger.debug("[Post-Review] Checking HTML script references after comprehensive review...")
                all_files = self._fix_html_script_reference_for_vanilla_js(all_files)
            
            # 総合レビュー後のQuality再評価
            quality_scores = self._assess_code_quality(
                all_files,
                complexity,
                goal,
                lint_result=lint_result
            )
            
            # Quality assessment & Comprehensive Review 完了
            hud.complete("Quality assessment & Comprehensive Review")
            
            # 完了
            hud.finish(success=True)
            return (all_files, lint_result, quality_scores)
            
        except Exception as e:
            # ⭐ デバッグログ追加: 例外の詳細を記録
            import traceback
            logger.error(f"[Multi-stage] ❌❌❌ EXCEPTION CAUGHT ❌❌❌")
            logger.error(f"[Multi-stage]   Exception message: {e}")
            logger.error(f"[Multi-stage]   Exception type: {type(e).__name__}")
            logger.error(f"[Multi-stage]   Exception args: {e.args}")
            logger.error(f"[Multi-stage]   Full traceback:\n{traceback.format_exc()}")
            logger.error(f"[Multi-stage]   all_files at exception: {list(all_files.keys()) if all_files else 'empty'}")
            
            hud.finish(success=False)
            logger.debug(f"   ❌ Multi-stage generation error: {e}")
            logger.debug("   ⚠ Falling back to single-stage generation")
            
            # ⭐ デバッグログ追加: フォールバック開始
            logger.warning(f"[Multi-stage] ▶ Starting fallback single-stage generation...")
            logger.warning(f"[Multi-stage]   Original goal length: {len(goal)} chars")
            
            # フォールバック
            fallback_code = self._generate_code_implementation(
                goal=goal,
                analysis="",
                plan="",
                complexity=complexity,
                skip_validation=False,
                skip_multi_stage=True
            )
            
            # ⭐ デバッグログ追加: フォールバック結果
            logger.warning(f"[Multi-stage] ◀ Fallback generation completed")
            logger.warning(f"[Multi-stage]   Fallback result: {list(fallback_code.keys()) if fallback_code else 'empty/None'}")
            logger.warning(f"[Multi-stage]   Fallback file count: {len(fallback_code) if fallback_code else 0}")
            
            # フォールバック時はlint_result=None, quality_scores={}を返す
            return (fallback_code, None, {})


    def _generate_code_implementation(
        self, 
        goal: str,
        analysis: str = "",
        plan: str = "",
        complexity: str = "medium",
        skip_validation: bool = False,
        force_filename: str = None,
        skip_multi_stage: bool = False
    ) -> Dict[str, str]:
        """
        LLMを使用してコードを生成
        
        Args:
            goal: 実装目標の説明
            analysis: 分析結果(オプション)
            plan: 実装計画(オプション)
            complexity: 複雑度("simple", "medium", "complex")
            skip_validation: 検証をスキップするか
            force_filename: 強制的に使用するファイル名
                        ⭐ 指定時はLLMが何を返しても、このファイル名で保存
        
        Returns:
            {filename: code} の辞書
        """
        
        # ========================================
        # 既存ファイルのコンテキストを準備
        # ========================================
        existing_context = ""
        
        # 🔍 デバッグ: _complexity_assessor の存在確認
        logger.debug(f"[EXISTING_CONTEXT] Step 1: hasattr(self, '_complexity_assessor') = {hasattr(self, '_complexity_assessor')}")
        
        if hasattr(self, '_complexity_assessor'):
            # 🔍 デバッグ: existing_files 属性の存在確認
            logger.debug(f"[EXISTING_CONTEXT] Step 2: hasattr(_complexity_assessor, 'existing_files') = {hasattr(self._complexity_assessor, 'existing_files')}")
            
            if hasattr(self._complexity_assessor, 'existing_files'):
                # 🔍 デバッグ: existing_files の内容確認
                logger.debug(f"[EXISTING_CONTEXT] Step 3: existing_files type = {type(self._complexity_assessor.existing_files)}")
                logger.debug(f"[EXISTING_CONTEXT] Step 3: existing_files bool = {bool(self._complexity_assessor.existing_files)}")
                logger.debug(f"[EXISTING_CONTEXT] Step 3: existing_files keys = {list(self._complexity_assessor.existing_files.keys())}")
                logger.debug(f"[EXISTING_CONTEXT] Step 3: existing_files length = {len(self._complexity_assessor.existing_files)}")
            else:
                logger.debug(f"[EXISTING_CONTEXT] Step 2: _complexity_assessor has NO 'existing_files' attribute")
        else:
            logger.debug(f"[EXISTING_CONTEXT] Step 1: self has NO '_complexity_assessor' attribute")
        
        # self.existing_files が存在する場合、コンテキストを構築
        if hasattr(self, '_complexity_assessor') and \
            hasattr(self._complexity_assessor, 'existing_files') and \
            self._complexity_assessor.existing_files:
                # 🔍 デバッグ: 条件成功
                logger.debug(f"[EXISTING_CONTEXT] ✅ All conditions passed - building existing_context")
                
                existing_context = "\nEXISTING FILES IN PROJECT:\n"
                for filename, code in self._complexity_assessor.existing_files.items():
                    # 🔍 デバッグ: 各ファイル処理
                    logger.debug(f"[EXISTING_CONTEXT] Processing file: {filename} ({len(code)} chars)")
                    
                    # ファイルサイズに応じて適切な量を渡す
                    max_chars = 5000  # 500→5000に増加
                    code_preview = code[:max_chars]
                    if len(code) > max_chars:
                        # 先頭と末尾の両方を含める
                        half = max_chars // 2
                        code_preview = code[:half] + "\n\n... [middle part omitted] ...\n\n" + code[-half:]
                
                    existing_context += f"\n## File: {filename}\n```\n{code_preview}\n```\n"
        else:
            # 🔍 デバッグ: 条件失敗
            logger.debug(f"[EXISTING_CONTEXT] ❌ Condition FAILED - existing_context remains empty")
        
        # 🔍 デバッグ: 最終結果
        logger.debug(f"[EXISTING_CONTEXT] Final existing_context length: {len(existing_context)} chars")
        

        
        # ========================================
        # ⭐ force_multiple_files_temp の初期化
        # ========================================
        # スコープエラーを防ぐため、if-else の前で初期化
        force_multiple_files_temp = False
        
        # ========================================
        # ⭐ force_filename が指定されている場合
        # ========================================
        if force_filename:
            prompt = f"""Generate code for the following requirements.

PRIMARY FILE: {force_filename}

REQUIREMENTS:
{goal}

INSTRUCTIONS:
1. The primary target file is: {force_filename}
2. You MUST include modifications to {force_filename}
3. You MAY also modify other related files shown in the repository map
4. Use the repository map to identify ALL files that need changes
5. Generate ALL necessary files to fulfill the requirements completely

OUTPUT FORMAT:
## File: {force_filename}
```
[your code here]
```

If additional files are needed:
## File: additional_file.ext
```
[additional code here]
```

{existing_context if existing_context else ''}

Generate the complete, working implementation now.
"""
        else:
            # ========================================
            # 既存のプロンプト構築ロジック
            # ========================================
            goal_lower = goal.lower()
            
            # ⭐ Multi-File Project Detection は Repository Analyzer初期化後に実行
            # (この時点では multi_file_detection は未定義)
            
            # 暫定的にキーワードベースのチェックのみ
            force_multiple_files_temp = (
                not skip_validation and (
                    any(keyword in goal_lower for keyword in [
                        "multi-file", "multiple files", "several files",
                        "create files", "full project", "complete project"
                    ])
                )
            )
            
            # 基本プロンプトの構築は Repository Analyzer初期化後に実行
            # (multi_file_detectionの結果を反映するため)

        # ========================================
        # ⭐⭐⭐ LLM呼び出しとレスポンス変換 ⭐⭐⭐
        # ========================================
        
        # LLMが利用できない場合のチェック
        if not self.llm_manager:
            logger.debug("   ⚠ LLM not available, using fallback code generation")
            return self._generate_fallback_code(goal)

        # 必要なモジュールをインポート(tryブロックの外で)
        import signal
        import traceback

        try:
            # ⭐ repository_analyzer の状態を確認
            if self.repository_analyzer:
                logger.debug(f"[VERIFY] repository_analyzer exists")
                
                
                # Repository分析はexecute_semi_auto_implementation内で実行済み
                # ここでは状態確認のみ
                repo_data_count = len(self.repository_analyzer.memory.repository_data)
                logger.debug(f"[VERIFY] repository_data: {repo_data_count} files")
                
                # デバッグ: repository_dataが空の場合（新規プロジェクトでは正常）
                if repo_data_count == 0:
                    logger.debug(
                        "[DEBUG] _generate_code_implementation called with empty repository_data (new project). "
                        "Multi-File Detection may not work correctly if existing files are present. "
                        "This function should be called after Repository analysis."
                    )
            else:
                logger.debug("[VERIFY] repository_analyzer is None")


            # ⭐ Multi-File Detection をRepository Analyzer初期化後に実行
            # force_filenameが指定されている場合はスキップ
            if force_filename:
                multi_file_detection = {'required': False, 'reason': 'force_filename specified', 'file_types': []}
                logger.debug(f"[Multi-File Detection] Skipped due to force_filename: {force_filename}")
            else:
                # 🆕 _project_structure を参照（再判定なし）
                if hasattr(self, '_project_structure') and self._project_structure:
                    file_types = self._project_structure.file_types
                    multi_file_detection = {
                        'required': bool(file_types),  # file_typesがあればTrue
                        'file_types': file_types,
                        'reason': self._project_structure.reason
                    }
                    logger.debug(f"[Prompt Construction] Using cached project_structure: required={multi_file_detection['required']}, file_types={file_types}")
                else:
                    # フォールバック: 既存のキーワード検出（_project_structureが未初期化の場合）
                    multi_file_detection = self._detect_multi_file_requirement(goal)
                    logger.debug(f"[Prompt Construction] Fallback to keyword detection: required={multi_file_detection['required']}, file_types={multi_file_detection.get('file_types', [])}")
                    
                    # フォールバック時のみフレームワークフィルタリング実行
                    if getattr(self, '_no_framework_mode', False):
                        goal_lower_local = goal.lower()
                        # 明示的にReact/Vueも要求されている場合はハイブリッド構成と判断
                        explicit_framework_request = (
                            ('react' in goal_lower_local and ('and react' in goal_lower_local or 'with react' in goal_lower_local or 'react for' in goal_lower_local)) or
                            ('vue' in goal_lower_local and ('and vue' in goal_lower_local or 'with vue' in goal_lower_local or 'vue for' in goal_lower_local))
                        )
                        
                        if not explicit_framework_request:
                            original_types = multi_file_detection.get('file_types', [])
                            framework_types = ('jsx', 'tsx', 'vue')
                            filtered_types = [ft for ft in original_types if ft not in framework_types]
                            
                            if filtered_types != original_types:
                                removed = [ft for ft in original_types if ft in framework_types]
                                logger.debug(f"[Framework Restriction] Removed framework file types: {removed}")
                                logger.debug(f"[Framework Restriction] file_types: {original_types} -> {filtered_types}")
                                multi_file_detection['file_types'] = filtered_types
                        else:
                            logger.debug(f"[Framework Restriction] Hybrid mode detected - keeping framework file types")

            # ⭐ multi_file_detectionの結果を反映
            force_multiple_files = force_multiple_files_temp or multi_file_detection['required']
            logger.debug(f"[Prompt Construction] force_multiple_files={force_multiple_files} (temp={force_multiple_files_temp}, detected={multi_file_detection['required']})")
            
            # ========================================
            # ⭐ プロンプト構築 (multi_file_detectionの結果を使用)
            # ========================================
            
            # 基本プロンプトの構築
            if existing_context and not force_multiple_files:
                # 単一ファイル修正モード
                logger.debug("[Prompt Construction] Single file modification mode")
                prompt = f"""MODIFY the existing file to achieve this goal: {goal}

            EXISTING FILE CONTENT:
            {existing_context}

            INSTRUCTIONS:
            1. You MUST preserve the existing code structure
            2. Only make necessary changes to fulfill the goal
            3. Do NOT completely rewrite the file
            4. Keep all existing functionality intact
            5. Add comments explaining what you changed

            {analysis if analysis else ''}

            {plan if plan else ''}

            Return the MODIFIED version of the file.
            """
            else:
                # 複数ファイルモード (新規作成または複数ファイル修正)
                logger.debug(f"[Prompt Construction] Multiple files mode (existing_context={'exists' if existing_context else 'none'}, force_multiple_files={force_multiple_files})")
                prompt = f"""Generate code to achieve this goal: {goal}

            {existing_context if existing_context else ''}

            {analysis if analysis else ''}

            {plan if plan else ''}
            """
            
            # 解決策1: Multi-File Detectionの結果からフレームワーク情報を抽出してプロンプトに追加
            # 優先順位1: multi_file_detection['framework'] から直接取得
            detected_framework = multi_file_detection.get('framework')
            
            # 優先順位2: reasonからフレームワークを抽出（フォールバック）
            if not detected_framework and multi_file_detection.get('reason'):
                reason = multi_file_detection['reason']
                # reasonからフレームワークを抽出
                if 'React' in reason or 'react' in reason:
                    detected_framework = 'React'
                elif 'Vue' in reason or 'vue' in reason:
                    detected_framework = 'Vue'
                elif 'Angular' in reason or 'angular' in reason:
                    detected_framework = 'Angular'
                elif 'Svelte' in reason or 'svelte' in reason:
                    detected_framework = 'Svelte'
                elif 'Flask' in reason or 'flask' in reason:
                    detected_framework = 'Flask'
                elif 'Django' in reason or 'django' in reason:
                    detected_framework = 'Django'
                elif 'FastAPI' in reason or 'fastapi' in reason:
                    detected_framework = 'FastAPI'
            
            # 優先順位3: goalからフレームワークを検出（追加）
            if not detected_framework:
                goal_lower = goal.lower()
                if 'flask' in goal_lower:
                    detected_framework = 'Flask'
                elif 'django' in goal_lower:
                    detected_framework = 'Django'
                elif 'fastapi' in goal_lower:
                    detected_framework = 'FastAPI'
                elif 'react' in goal_lower:
                    detected_framework = 'React'
                elif 'vue' in goal_lower:
                    detected_framework = 'Vue'
            
            # デバッグログ追加
            if detected_framework:
                logger.debug(f"[Prompt Construction] Framework detected: {detected_framework} (from {'framework' if multi_file_detection.get('framework') else 'reason/goal'})")
            
            # 🆕 フロントエンドフレームワーク（React/Vue/Angular/Svelte）は_no_framework_mode時にスキップ
            frontend_frameworks = {'React', 'Vue', 'Angular', 'Svelte'}
            skip_framework_prompt = (
                getattr(self, '_no_framework_mode', False) and 
                detected_framework in frontend_frameworks
            )
            
            if skip_framework_prompt:
                logger.debug(f"[Prompt Construction] SKIPPING {detected_framework} framework prompt due to no-framework mode")
                detected_framework = None  # フレームワークプロンプトを完全にスキップ
            
            # フレームワークが検出された場合、プロンプトに追加
            if detected_framework:
                logger.debug(f"[Prompt Construction] Detected framework: {detected_framework}")
                
                framework_prompts = {
                    'Flask': """
⚠️ FRAMEWORK REQUIREMENT: FLASK
This project MUST follow Flask conventions.

FLASK STATIC FILE RULES (CRITICAL - #1 CAUSE OF BROKEN APPS):
1. Static files are served from /static/ URL path by default
2. In HTML files, ALWAYS use absolute paths with /static/ prefix:
   ✅ CORRECT: href="/static/css/styles.css"
   ✅ CORRECT: src="/static/js/app.js"
   ❌ WRONG:   href="css/styles.css"  (relative path will 404!)
   ❌ WRONG:   src="js/app.js"        (relative path will 404!)

3. WHY this matters:
   - Flask serves index.html at URL '/' via send_from_directory
   - Browser resolves relative paths from '/', not '/static/'
   - Relative path 'css/styles.css' becomes '/css/styles.css' (404!)
   - Absolute path '/static/css/styles.css' always works

4. Directory structure:
   static/
     css/styles.css    → accessed at /static/css/styles.css
     js/app.js         → accessed at /static/js/app.js
     index.html        → served at '/' but links must use /static/

5. For Jinja2 templates, prefer url_for():
   <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">

FLASK-SQLALCHEMY RULES:
1. db.create_all() MUST be inside app_context():
   with app.app_context():
       db.create_all()

2. NEVER use 'metadata' as column name (SQLAlchemy reserved)
3. NEVER use 'query' as column name (Flask-SQLAlchemy reserved)

⚠️ CRITICAL: Relative paths in Flask HTML files = broken CSS/JS = unusable app

""",
                    'Django': """
⚠️ FRAMEWORK REQUIREMENT: DJANGO
This project MUST use Django framework.

DJANGO STATIC FILE RULES:
1. Use {% load static %} in templates
2. Reference static files with {% static 'path/to/file' %}
3. Configure STATIC_URL and STATICFILES_DIRS in settings.py

""",
                    'FastAPI': """
⚠️ FRAMEWORK REQUIREMENT: FASTAPI
This project MUST use FastAPI framework.

FASTAPI REQUIREMENTS:
1. Use async def for route handlers when possible
2. Use Pydantic models for request/response validation
3. Mount static files with StaticFiles middleware

""",
                    'React': """
⚠️ FRAMEWORK REQUIREMENT: REACT
This project MUST use React framework.

MANDATORY REACT REQUIREMENTS:
1. ALL JavaScript files in src/ must import React:
   import React from 'react';

2. Create React components (functional or class):
   - Functional: function ComponentName() { ... } or const ComponentName = () => { ... }
   - Class: class ComponentName extends React.Component { ... }

3. Use JSX syntax for UI elements:
   - Return JSX: return <div>...</div>;
   - NOT vanilla DOM manipulation (no document.getElementById, etc.)

4. Export components:
   - export default ComponentName;

5. For index.js/main entry point:
   - Import ReactDOM: import ReactDOM from 'react-dom/client';
   - Render root: const root = ReactDOM.createRoot(document.getElementById('root'));
   - Use: root.render(<App />);

⚠️ CRITICAL: Every .js/.jsx/.tsx file in src/ directory MUST be React code with proper imports and JSX.

""",
                    'Vue': """
⚠️ FRAMEWORK REQUIREMENT: VUE
This project MUST use Vue framework.

MANDATORY VUE REQUIREMENTS:
1. Create Vue components with proper structure
2. Use Vue's template syntax
3. Export components properly

""",
                    'Angular': """
⚠️ FRAMEWORK REQUIREMENT: ANGULAR
This project MUST use Angular framework.

MANDATORY ANGULAR REQUIREMENTS:
1. Create Angular components with TypeScript
2. Use Angular decorators (@Component, etc.)
3. Follow Angular module structure

""",
                    'Svelte': """
⚠️ FRAMEWORK REQUIREMENT: SVELTE
This project MUST use Svelte framework.

MANDATORY SVELTE REQUIREMENTS:
1. Create Svelte components with .svelte extension
2. Use Svelte's reactive syntax
3. Export components properly

"""
                }
                
                prompt = framework_prompts.get(detected_framework, '') + prompt
                
                # ==========================================
                # 【Solution 4】Reactプロジェクト向け追加指示
                # ==========================================
                if detected_framework == 'React':
                    react_additional_prompt = """
⚠️ REACT PROJECT MANDATORY FILES:

1. package.json (REQUIRED):
   - MUST include React dependencies
   - MUST include scripts for start/build
   - Example structure:
     {
       "name": "react-app",
       "version": "0.1.0",
       "dependencies": {
         "react": "^18.0.0",
         "react-dom": "^18.0.0"
       },
       "scripts": {
         "start": "react-scripts start",
         "build": "react-scripts build"
       },
       "devDependencies": {
         "react-scripts": "^5.0.0"
       }
     }

2. Component Structure:
   - EACH component should have its own .jsx/.js file
   - If goal specifies "components: TodoList, TodoItem, AddTodo"
     → Create separate files: TodoList.jsx, TodoItem.jsx, AddTodo.jsx
   - EACH component should have a corresponding .css file if styling is needed
   - If component imports CSS (e.g., "import './TodoItem.css'")
     → MUST create that CSS file

3. File Organization:
   - Components in src/components/ or src/ directory
   - Styles in src/styles/ or alongside components
   - ALWAYS create ALL files that are imported

⚠️ CRITICAL CHECKLIST:
✓ package.json exists with React dependencies
✓ Each component has its own file
✓ Each imported CSS file is created
✓ No missing file imports
✓ All components are properly exported

"""
                    prompt = react_additional_prompt + prompt
                    logger.debug("[Prompt Construction] Added React project mandatory files instruction")
            
            
            # ==========================================
            # 【改善】App Patterns プロンプト強化（app_patterns.json活用）
            # ==========================================
            # AppPatternDetectorを使ってgoalからパターンを検出
            app_pattern_detector = AppPatternDetector(logger=logger)
            detected_app_pattern = app_pattern_detector.detect_pattern(goal)
            
            if detected_app_pattern:
                logger.debug(f"[Prompt Construction] App pattern detected: {detected_app_pattern}")
                pattern_enhancement = app_pattern_detector.get_prompt_enhancement(detected_app_pattern)
                if pattern_enhancement:
                    prompt = pattern_enhancement + "\n\n" + prompt
                    logger.debug(f"[Prompt Construction] Added app pattern enhancement ({len(pattern_enhancement)} chars)")
            
            # detected_pattern に基づくプロンプト強化（フォールバック）
            if not detected_app_pattern and multi_file_detection.get('detected_pattern'):
                pattern = multi_file_detection['detected_pattern']
                enhancement = pattern.get('prompt_enhancement')
                
                if enhancement:
                    logger.debug(f"[Prompt Construction] Adding app pattern enhancement")
                    
                    # prompt_enhancementがDict（改善版）の場合
                    if isinstance(enhancement, dict):
                        # Base instruction
                        base_instruction = enhancement.get('base', '')
                        if base_instruction:
                            prompt += f"\n\n⚠️ APPLICATION PATTERN REQUIREMENT:\n{base_instruction}\n"
                        
                        # File requirements
                        files_section = enhancement.get('files', {})
                        if files_section:
                            prompt += "\n### File Requirements:\n\n"
                            for file_type, file_spec in files_section.items():
                                if isinstance(file_spec, dict):
                                    prompt += f"**{file_spec.get('filename', file_type)}:**\n"
                                    prompt += f"Purpose: {file_spec.get('purpose', 'N/A')}\n"
                                    
                                    requirements = file_spec.get('requirements', [])
                                    if requirements:
                                        prompt += "Requirements:\n"
                                        for req in requirements:
                                            prompt += f"- {req}\n"
                                    
                                    # Anti-patterns
                                    anti_patterns = file_spec.get('anti_patterns', [])
                                    if anti_patterns:
                                        prompt += "\n❌ AVOID:\n"
                                        for ap in anti_patterns:
                                            prompt += f"- {ap}\n"
                                    prompt += "\n"
                        
                        # Quality requirements
                        quality_reqs = enhancement.get('quality_requirements', {})
                        if quality_reqs:
                            prompt += "### Quality Requirements:\n"
                            for key, value in quality_reqs.items():
                                prompt += f"- {key}: {value}\n"
                    
                    # prompt_enhancementが文字列（従来版）の場合
                    elif isinstance(enhancement, str):
                        prompt += f"\n\n⚠️ APPLICATION PATTERN REQUIREMENT:\n{enhancement}\n"

            # 複数ファイルの場合の指示を追加（ルールファイルベース）
            if force_multiple_files:
                logger.debug("[Prompt Construction] Adding multi-file instruction (rule-based)")
                
                # Multi-File Detection の結果から expected_files を構築
                if multi_file_detection['required']:
                    # goalからファイル名を抽出
                    common_extensions = [
                        'py', 'js', 'jsx', 'ts', 'tsx',
                        'html', 'css', 'scss', 'sass', 'less',
                        'json', 'xml', 'yaml', 'yml',
                        'md', 'txt', 'sh', 'bat',
                        'java', 'cpp', 'c', 'h', 'hpp',
                        'go', 'rs', 'rb', 'php', 'swift', 'kt',
                        'sql', 'db', 'ini', 'conf', 'cfg', 'env'
                    ]
                    extensions_pattern = '|'.join(common_extensions)
                    detected_filenames = re.findall(rf'([\w\-]+\.(?:{extensions_pattern}))\b', goal.lower())
                    
                    expected_files = []
                    for filename in detected_filenames:
                        # 拡張子を抽出
                        ext = filename.split('.')[-1]
                        expected_files.append(FileRequirement(
                            filepath=filename,
                            file_type=ext,
                            reason=f"Explicitly mentioned in goal",
                            priority="required"
                        ))
                    
                    # ⭐ 新規追加: file_types から expected_files を自動構築
                    # Goal に明示的なファイル名がない場合、file_types からデフォルトファイルを生成
                    detected_file_types = multi_file_detection.get('file_types', [])
                    if detected_file_types and not expected_files:
                        logger.debug(f"[Prompt Construction] Building expected_files from file_types: {detected_file_types}")
                        
                        # file_types に基づいてデフォルトファイルを追加
                        default_files_for_types = {
                            'html': ('index.html', 'html'),
                            'css': ('styles.css', 'css'),
                            'js': ('app.js', 'js'),
                            'jsx': ('src/App.jsx', 'jsx'),
                            'ts': ('src/main.ts', 'ts'),
                            'tsx': ('src/App.tsx', 'tsx'),
                            'python': ('main.py', 'py'),
                        }
                        
                        for file_type in detected_file_types:
                            if file_type in default_files_for_types:
                                default_file, ext = default_files_for_types[file_type]
                                expected_files.append(FileRequirement(
                                    filepath=default_file,
                                    file_type=ext,
                                    reason=f"Required for web project (detected file_type: {file_type})",
                                    priority="required"
                                ))
                        
                        logger.debug(f"[Prompt Construction] Auto-generated expected_files from file_types: {[f.filepath for f in expected_files]}")
                    
                    # HTML/Webプロジェクトの場合、index.htmlを自動追加
                    goal_lower = goal.lower()
                    web_keywords = ['landing page', 'website', 'web page', 'web app', 'webapp', 'home page', 'portfolio']
                    css_detected = any(f.file_type in ['css', 'scss', 'sass', 'less'] for f in expected_files)
                    html_detected = any(f.file_type == 'html' for f in expected_files)
                    is_web_project = any(keyword in goal_lower for keyword in web_keywords)
                    
                    if is_web_project and css_detected and not html_detected:
                        # CSSファイルがあるのにHTMLがない → index.htmlを追加
                        expected_files.append(FileRequirement(
                            filepath='index.html',
                            file_type='html',
                            reason=f"Inferred from web project type and CSS files",
                            priority="required"
                        ))
                        logger.debug("[Prompt Construction] Auto-added index.html for web project")
                    
                    logger.debug(f"[Prompt Construction] Constructed expected_files: {[f.filepath for f in expected_files]}")
                else:
                    expected_files = None
                
                multi_file_prompt = self._build_multi_file_prompt(
                    expected_files=expected_files,
                    max_phase=3  # Phase 1-3のすべてのルールを適用
                )
                prompt += multi_file_prompt

            # Phase 1.1: 多段階生成の判定は execute_semi_auto_implementation で実行済み
            # ここでは skip_multi_stage=True で呼ばれるため、この条件は常にFalse
            # （念のため条件を残すが、実際には通過しない）
            # ===== デバッグログ =====
                        # =======================
            
            if complexity == "complex" and multi_file_detection['required'] and not skip_multi_stage:
                # この分岐は到達しないはず（execute_semi_auto_implementationで処理済み）
                logger.warning("Unexpected multi-stage generation trigger in _generate_code_implementation")
                result = self._execute_multi_stage_generation(goal, complexity)
                # タプルの最初の要素（all_files）のみを返す
                return result[0] if result else {}

            # ⭐ プロジェクトコンテキストを取得(repository_analyzerのデータを含む)
            project_context = self._get_project_context()
            logger.debug(f"[LLM] Project context length: {len(project_context)} chars")
            logger.debug(f"[VERIFY] Project context length: {len(project_context)} chars")
            logger.debug(f"[VERIFY] Project context content:\n{project_context}")  #

            
            # デバッグログ: LLM呼び出し開始
            logger.debug(f"[VERIFY] Starting code generation for goal: {goal[:100]}...")  #
            logger.debug(f"[LLM] Starting code generation for goal: {goal[:100]}...")
            logger.debug(f"[VERIFY] Prompt length: {len(prompt)} chars")  #
            logger.debug(f"[LLM] Prompt length: {len(prompt)} chars")
        
            # LLM呼び出し(スピナー付き)
            logger.debug("[LLM] Entering Spinner context...")
            
             # Phase 1.1: StepProgressのスピナーのみを使用（pause/resume削除）
            try:
                logger.debug("[LLM] Calling llm_manager.generate_response()...")                
            
                def timeout_handler(signum, frame):
                    raise TimeoutError("LLM generation timed out after 60 seconds")
            
                # タイムアウト設定(Linuxのみ)
                if hasattr(signal, 'SIGALRM'):
                    signal.signal(signal.SIGALRM, timeout_handler)
                    signal.alarm(60)  # 60秒タイムアウト
            
                # 改善策A: 複雑性に応じてmax_tokensを設定
                if complexity == "simple":
                    max_tokens = 2000
                elif complexity == "medium":
                    max_tokens = 4000
                elif complexity == "complex":
                    max_tokens = 8000
                else:
                    max_tokens = 4000
                
                # Multi-File生成の場合、max_tokensを増やす（複数ファイル生成には十分なトークンが必要）
                # 
                # 修正: 2025-12-29, 2025-01-22
                # 問題: styles.css等が途中で途切れる（max_tokens不足）
                # 対策: 各複雑度のmax_tokensを増加
                #   - simple: 8000 → 16000 (2025-01-22: 12000→16000に再増加)
                #   - medium: 12000 → 16000
                #   - complex: 16000 → 変更なし
                if force_multiple_files and multi_file_detection['required']:
                    # 予想ファイル数に応じて増やす
                    # 注意: max_tokens > 16000 は Anthropic API で
                    # "Streaming is required for operations that may take longer than 10 minutes"
                    # エラーを引き起こす可能性がある
                    if complexity == "simple":
                        max_tokens = 16000
                    elif complexity == "medium":
                        max_tokens = 16000
                    elif complexity == "complex":
                        max_tokens = 16000
                    logger.debug(f"[LLM] Multi-file detected, increased max_tokens to {max_tokens}")
                
                logger.debug(f"[LLM] Complexity: {complexity}, max_tokens: {max_tokens}")
                
                try:
                    response = self.llm_manager.generate_response(
                        prompt=prompt,
                        context=project_context,  # ⭐ プロジェクトコンテキストをLLMに送信
                        system_prompt=self._get_code_generation_system_prompt(
                            goal_constraints=getattr(self, '_current_goal_constraints', None)
                        ),
                        max_tokens=max_tokens
                    )
                    logger.debug(f"[LLM] Response received. Type: {type(response)}")
                    
                    # ⭐ デバッグログ: 真の原因特定用
                    # LLMResponse: finish_reason (not stop_reason), usage is Dict
                    if hasattr(response, 'finish_reason'):
                        logger.debug(f"[LLM] finish_reason: {response.finish_reason}")
                    if hasattr(response, 'usage') and isinstance(response.usage, dict):
                        usage = response.usage
                        input_tokens = usage.get('prompt_tokens', 'N/A')
                        output_tokens = usage.get('completion_tokens', 'N/A')
                        logger.debug(f"[LLM] usage: input_tokens={input_tokens}, output_tokens={output_tokens}, max_tokens={max_tokens}")
                    if hasattr(response, 'content') and response.content:
                        tail = response.content[-200:] if len(response.content) > 200 else response.content
                        logger.debug(f"[LLM] output_tail (last 200 chars): {repr(tail)}")
                    
                    # ⭐ 続き生成: finish_reason == 'max_tokens' or 'length' の場合、続きを自動生成
                    # 参考: Azure OpenAI/一般的なLLMツールの実装パターン
                    # 注: OpenAI APIは'length'、Anthropic APIは'max_tokens'を返す
                    accumulated_content = response.content if hasattr(response, 'content') else ""
                    continuation_count = 0
                    max_continuations = 5  # 無限ループ防止
                    
                    while (hasattr(response, 'finish_reason') and 
                           response.finish_reason in ('max_tokens', 'length') and 
                           continuation_count < max_continuations):
                        continuation_count += 1
                        logger.debug(f"[LLM] Continuation {continuation_count}/{max_continuations}: finish_reason was max_tokens, requesting continuation...")
                        
                        # 続き生成プロンプト: 末尾1000文字をコンテキストとして渡す
                        context_tail = accumulated_content[-1000:] if len(accumulated_content) > 1000 else accumulated_content
                        continuation_prompt = f"""Continue generating code from where you left off.

The previous output ended with:
```
{context_tail}
```

IMPORTANT:
- Continue from EXACTLY where you stopped
- Do NOT repeat any code that was already generated
- Do NOT add any preamble or explanation, just continue the code
- Maintain the same file format (```filename markers)"""
                        
                        # ⭐ 修正: 元のGoalとLLMの途中までの回答をconversation_historyとして渡す
                        # これにより、LLMは「何を生成しようとしていたか」を把握した状態で続きを生成できる
                        conversation_history_for_continuation = [
                            {"role": "user", "content": prompt},  # 元のGoal
                            {"role": "assistant", "content": accumulated_content}  # LLMの途中までの回答
                        ]
                        
                        continuation_response = self.llm_manager.generate_response(
                            prompt=continuation_prompt,
                            context=project_context,
                            system_prompt=self._get_code_generation_system_prompt(
                                goal_constraints=getattr(self, '_current_goal_constraints', None)
                            ),
                            max_tokens=max_tokens,
                            conversation_history=conversation_history_for_continuation  # ⭐ 追加
                        )
                        
                        # デバッグログ
                        if hasattr(continuation_response, 'finish_reason'):
                            logger.debug(f"[LLM] Continuation {continuation_count}: finish_reason: {continuation_response.finish_reason}")
                        if hasattr(continuation_response, 'usage') and isinstance(continuation_response.usage, dict):
                            c_usage = continuation_response.usage
                            c_output = c_usage.get('completion_tokens', 'N/A')
                            logger.debug(f"[LLM] Continuation {continuation_count}: output_tokens={c_output}")
                        
                        # コンテンツを累積
                        if hasattr(continuation_response, 'content') and continuation_response.content:
                            # ⭐ 修正: continuation出力の先頭にあるファイルヘッダーとコードブロックマーカーを除去
                            # LLMが続きを生成する際に新しい「## File: xxx」や```マーカーを付けてしまう問題を修正
                            # これを残すと_extract_code_blocksが2つのセクションとして解釈し、
                            # 後者が前者を上書きしてHTMLの前半が失われる
                            cont_content = continuation_response.content
                            # パターン1: 先頭の「## File: xxx」ヘッダー + コードブロックマーカーを除去
                            cont_content = re.sub(r'^(?:##\s*File:\s*[^\n]*\r?\n)?```\w*\r?\n?', '', cont_content)
                            # パターン2: 先頭が「## File: xxx」のみ（```なし）の場合も除去
                            cont_content = re.sub(r'^##\s*File:\s*[^\n]*\r?\n', '', cont_content)
                            accumulated_content += cont_content
                            logger.debug(f"[LLM] Continuation {continuation_count}: Added {len(cont_content)} chars, total: {len(accumulated_content)}")
                        
                        # 次のループ判定用にresponseを更新
                        response = continuation_response
                    
                    # 累積コンテンツでresponseを更新（続き生成があった場合）
                    if continuation_count > 0:
                        if hasattr(response, 'content'):
                            response.content = accumulated_content
                        logger.debug(f"[LLM] Continuation completed: {continuation_count} additional call(s), final content length: {len(accumulated_content)} chars")
                    
                finally:
                    # タイムアウト解除
                    if hasattr(signal, 'SIGALRM'):
                        signal.alarm(0)
            except Exception as e:
                logger.error(f"[LLM] Generation failed: {e}")
                raise

            logger.debug("[LLM] LLM call completed successfully")
        
            # ⭐⭐⭐ ここが最重要: LLMResponse → 文字列変換 ⭐⭐⭐
            if hasattr(response, 'content'):
                # LLMResponse.content 属性を取得
                response_text = response.content
                logger.debug(f"[LLM] Extracted content from response.content (length: {len(response_text)})")
            elif isinstance(response, str):
                # 既に文字列の場合
                response_text = response
                logger.debug(f"[LLM] Response is already string (length: {len(response_text)})")
            else:
                # その他の場合は str() で変換
                response_text = str(response)
                logger.debug(f"[LLM] Converted response to string (length: {len(response_text)})")
        
        except TimeoutError as e:
            logger.error(f"[LLM] Timeout error: {e}")
            logger.debug(f"   ⚠ LLM generation timed out after 60 seconds")
            logger.debug(f"   ℹ️ Falling back to template code generation")
            return self._generate_fallback_code(goal)
        except Exception as e:
            logger.error(f"[LLM] Generation failed with exception: {type(e).__name__}: {e}")
            logger.debug(f"[LLM] Full traceback: {traceback.format_exc()}")
            logger.debug(f"   ⚠ LLM generation failed: {e}")
            logger.debug(f"   ℹ️ Falling back to template code generation")
            return self._generate_fallback_code(goal)
        
        
        # ========================================
        # ⭐ コードの抽出と整形(response_text を使用)
        # ========================================
        cleaned_files = self._extract_code_blocks(response_text)
        
        # ⭐ デバッグログ: _extract_code_blocks の結果
        logger.debug(f"[Code Extraction] _extract_code_blocks returned {len(cleaned_files)} file(s)")
        if cleaned_files:
            for fn, code in cleaned_files.items():
                logger.debug(f"[Code Extraction]   - {fn}: {len(code)} chars")
        else:
            logger.debug(f"[Code Extraction]   (empty result)")
        
        
        # ========================================
        # ⭐ force_filename が指定されている場合の強制修正
        # ========================================
        if force_filename:
            logger.debug(f"[Code Extraction] force_filename specified: {force_filename}")
            
            # cleaned_files にコードが含まれている場合
            if cleaned_files:
                logger.debug(f"[Code Extraction] Branch: cleaned_files has content")
                # LLMが返したファイル名に関わらず、force_filename で統一
                forced_files = {}
                first_code = None
                original_filename = None
                
                # 最初に見つかったコードを取得
                for filename, code in cleaned_files.items():
                    first_code = code
                    original_filename = filename
                    break
                
                if first_code:
                    # ファイル名が一致しない場合は警告を出力
                    if original_filename != force_filename:
                        logger.debug(f"[Auto-completion] LLM returned incorrect filename: {original_filename}")
                        logger.debug(f"[Auto-completion] Correcting to specified filename: {force_filename}")
                    
                    logger.debug(f"[Code Extraction] Returning {force_filename}: {len(first_code)} chars")
                    forced_files[force_filename] = first_code
                    return forced_files
                else:
                    logger.debug(f"[Code Extraction] WARNING: first_code is empty/None")
            
            # コードが抽出できなかった場合
            # (LLMが ## File: 形式を返さなかった場合)
            if response_text.strip():
                # レスポンス全体をコードとして扱う
                logger.debug("[Auto-completion] No ## File: format detected in LLM response")
                logger.debug(f"[Auto-completion] Using entire response as code for: {force_filename}")
                logger.debug(f"[Code Extraction] Branch: Fallback (response_text as code)")
                logger.debug(f"[Code Extraction] response_text length: {len(response_text)}")
                
                # レスポンスから余計な説明文を除去
                code_content = response_text.strip()
                
                # "Based on..." などの説明文を除去
                if code_content.startswith("Based on"):
                    lines = code_content.split('\n')
                    # 最初のコードブロックまたは実際のコードを探す
                    code_start = 0
                    for i, line in enumerate(lines):
                        if line.strip().startswith('const ') or \
                        line.strip().startswith('import ') or \
                        line.strip().startswith('module.') or \
                        line.strip().startswith('function ') or \
                        line.strip().startswith('class ') or \
                        line.strip().startswith('def ') or \
                        line.strip().startswith('package ') or \
                        line.strip().startswith('<?php') or \
                        line.strip().startswith('<!DOCTYPE'):
                            code_start = i
                            break
                    
                    if code_start > 0:
                        code_content = '\n'.join(lines[code_start:])
                
                # ⭐ 修正: マークダウン構文を除去（改善4）
                before_clean = len(code_content)
                code_content = self._clean_generated_code(code_content)
                logger.debug(f"[Code Extraction] After _clean_generated_code: {before_clean} -> {len(code_content)} chars")
                
                if code_content.strip():
                    logger.debug(f"[Code Extraction] Returning fallback code for {force_filename}")
                    return {force_filename: code_content}
                else:
                    logger.debug(f"[Code Extraction] WARNING: code_content became empty after cleaning!")
            else:
                logger.debug(f"[Code Extraction] Branch: Empty response, using template")
                # 完全に空の場合はテンプレートを返す
                logger.debug(f"   ⚠ LLM returned empty response")
                logger.debug(f"   ✅ Creating template file: {force_filename}")
                
                # ファイル拡張子から言語を判定
                ext = force_filename.split('.')[-1] if '.' in force_filename else ''
                
                if ext == 'js':
                    template = """// TODO: Implement this file

    module.exports = {};
    """
                elif ext == 'jsx':
                    template = """import React from 'react';

    const Component = () => {
    return (
        <div>
        {/* TODO: Implement this component */}
        </div>
    );
    };

    export default Component;
    """
                elif ext == 'py':
                    template = """# TODO: Implement this file

    def main():
        pass

    if __name__ == "__main__":
        main()
    """
                elif ext == 'html':
                    template = """<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO</title>
    </head>
    <body>
    <!-- TODO: Implement this page -->
    </body>
    </html>
    """
                elif ext == 'css':
                    template = """/* TODO: Implement styles */

    body {
    margin: 0;
    padding: 0;
    }
    """
                elif ext == 'json':
                    template = """{
    "todo": "Implement this configuration"
    }
    """
                else:
                    template = "// TODO: Implement this file\n"
                
                return {force_filename: template}
        
        
        # ========================================
        # force_filename が指定されていない場合(既存ロジック)
        # ========================================
        if not cleaned_files:
            # デフォルトファイル名の生成
            # 🆕 修正: response_textの実際のコンテンツからファイル種別を優先判定する。
            # 問題: LLMが「## File: neon_tetris.html」形式でHTMLを返したが
            # _extract_code_blocksが抽出に失敗した場合（finish_reason: length等）、
            # goalに「javascript」が含まれているとscript.jsと誤判定され、
            # auto-completion後もscript.jsがgenerated_codeに残り続ける。
            code_content = response_text.strip()
            
            # Step 1: response_textから「## File: xxx」ヘッダーを抽出してファイル名を取得
            file_header_match = re.match(r'^##\s*File:\s*(\S+)', code_content)
            if file_header_match:
                primary_file = file_header_match.group(1)
                logger.debug(f"[Code Extraction] Detected file header in response: {primary_file}")
                # ヘッダー行とコードブロックマーカーを除去
                lines = code_content.split('\n')
                clean_start = 0
                for i, line in enumerate(lines):
                    stripped = line.strip()
                    if stripped.startswith('## File:') or stripped.startswith('```'):
                        clean_start = i + 1
                    else:
                        break
                code_content = '\n'.join(lines[clean_start:])
                # 末尾の```も除去
                if code_content.rstrip().endswith('```'):
                    code_content = code_content.rstrip()[:-3].rstrip()
            else:
                # Step 2: コンテンツの実際の内容からファイル種別を判定
                content_lower = code_content[:2000].lower()
                
                if '<!doctype html' in content_lower or '<html' in content_lower:
                    primary_file = "index.html"
                    logger.debug("[Code Extraction] Content-based detection: HTML document")
                else:
                    # Step 3: goalキーワードによるフォールバック（従来ロジック）
                    goal_lower = goal.lower()
                    
                    if "python" in goal_lower or "py " in goal_lower:
                        primary_file = "main.py"
                    elif "javascript" in goal_lower or "js " in goal_lower:
                        primary_file = "script.js"
                    elif "html" in goal_lower:
                        primary_file = "index.html"
                    elif "react" in goal_lower:
                        primary_file = "App.jsx"
                    elif "css" in goal_lower:
                        primary_file = "style.css"
                    elif "typescript" in goal_lower or "ts " in goal_lower:
                        primary_file = "main.ts"
                    else:
                        primary_file = "code.txt"
            
            return {primary_file: code_content}
        

        # ========================================
        # Layer 3: 事後検証 + 自動補完
        # ========================================
        if not skip_validation and 'multi_file_detection' in locals() and multi_file_detection and multi_file_detection.get('required', False):
            logger.debug("[Code Generation] Starting post-generation validation...")
            logger.debug(f"[Post-Validation] Checking for incomplete multi-file generation...")
            cleaned_files = self.post_validator.validate_and_complete(
                generated_files=cleaned_files,
                goal=goal,
                multi_file_detection=multi_file_detection
            )
            logger.debug(f"[Code Generation] After validation: {len(cleaned_files)} files")
            logger.debug(f"[Post-Validation] Final file count: {len(cleaned_files)}")


        # 追加: ファイル間整合性チェック
        consistency_issues = self._validate_file_consistency(cleaned_files)
        if consistency_issues:
            logger.debug("\n⚠  File Consistency Issues Detected:")
            for issue in consistency_issues:
                logger.debug(f"   • {issue}")
            logger.debug("\n⚙ These issues may cause runtime errors and should be fixed.")

        return cleaned_files


    def _validate_file_consistency(self, files: Dict[str, str]) -> List[str]:
        """
        生成されたファイル間の整合性をチェック
        
        Args:
            files: {filename: code}
        
        Returns:
            整合性の問題リスト
        """
        issues = []
        
        # ⭐ 修正B: 未実装メソッド検出（pass only）
        issues.extend(self._check_unimplemented_methods(files))
        
        # HTMLとJavaScriptのペアを探す
        html_files = {f: c for f, c in files.items() if f.endswith('.html')}
        js_files = {f: c for f, c in files.items() if f.endswith('.js')}
        
        for html_file, html_code in html_files.items():
            for js_file, js_code in js_files.items():
                # HTMLから呼び出されているメソッドを抽出
                method_calls = re.findall(r'calculator\.(\w+)\(', html_code)
                
                # JavaScriptで定義されているメソッドを抽出
                method_definitions = re.findall(r'^\s*(\w+)\s*\([^)]*\)\s*\{', js_code, re.MULTILINE)
                
                # 呼び出されているが定義されていないメソッドを検出
                undefined_methods = set(method_calls) - set(method_definitions)
                for method in undefined_methods:
                    issues.append(
                        f"{html_file}: Method 'calculator.{method}()' is called but not defined in {js_file}"
                    )
                
                # data属性の値チェック（data-number などが空でないか）
                empty_data_attrs = re.findall(r'data-(\w+)>(?!\w)', html_code)
                if empty_data_attrs:
                    for attr in set(empty_data_attrs):
                        issues.append(
                            f"{html_file}: data-{attr} attribute has no value (should be data-{attr}=\"value\")"
                        )
        
        return issues
    
    def _check_unimplemented_methods(self, files: Dict[str, str]) -> List[str]:
        """
        ⭐ 修正B: 未実装メソッド（pass only）を検出
        
        def method_name(...):
            pass
        
        または
        
        def method_name(...):
            '''docstring'''
            pass
        
        のパターンを検出し、警告を返す。
        
        Args:
            files: {filename: code}
        
        Returns:
            未実装メソッドの警告リスト
        """
        issues = []
        
        for filename, code in files.items():
            if not filename.endswith('.py'):
                continue
            
            # パターン: def xxx(...): の後に pass のみ（docstringあり/なし）
            pattern = re.compile(
                r'def\s+(\w+)\s*\([^)]*\)\s*(?:->\s*[^:]+)?\s*:\s*\n'  # def xxx(...) -> type:
                r'(?:\s*(?:"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\')\s*\n)?'  # optional docstring
                r'\s*pass\s*(?:\n|$)',  # pass
                re.MULTILINE
            )
            
            matches = pattern.findall(code)
            
            for method_name in matches:
                # __init__ や __repr__ などの特殊メソッドで pass のみは許容
                if method_name.startswith('__') and method_name.endswith('__'):
                    continue
                
                issues.append(
                    f"{filename}: Method '{method_name}()' is not implemented (pass only)"
                )
                logger.debug(f"[Consistency] Unimplemented method detected: {filename}:{method_name}()")
        
        return issues


    def _build_multi_file_instruction(self, force: bool, file_types: List[str], attempt: int, detection_result: dict) -> str:
        """
        複数ファイル指示を構築(リトライ時に強度を上げる)
        
        Args:
            force: 強制フラグ
            file_types: 検出されたファイル種別
            attempt: リトライ回数 (0-indexed)
            detection_result: 検出結果
            
        Returns:
            プロンプト指示文字列
        """
        if not force:
            # 通常モード
            return """
    If this implementation would benefit from multiple files (e.g., separation of concerns,
    modularity, or maintainability), you may generate multiple files using this format:

    ## File: filename1.ext
    ```language
    [code]
    ```

    ## File: filename2.ext
    ```language
    [code]
    ```

    Otherwise, a single file is acceptable.
    """
        
        # 強制モード - リトライ回数に応じて強度を上げる
        if attempt == 0:
            # 1回目: 通常の強制指示
            emphasis = "CRITICAL REQUIREMENT"
            you_must = "You MUST"
        elif attempt == 1:
            # 2回目: 強調
            emphasis = "⚠ CRITICAL REQUIREMENT - SECOND ATTEMPT ⚠"
            you_must = "You are REQUIRED to"
        else:
            # 3回目: 最大強調
            emphasis = "🚨 FINAL CRITICAL REQUIREMENT - LAST ATTEMPT 🚨"
            you_must = "It is MANDATORY that you"
        
        # 検出されたファイル種別に応じた具体例を生成
        if 'html' in file_types and 'css' in file_types and 'js' in file_types:
            file_examples = """
    ## File: calculator.html
    ```html
    <!DOCTYPE html>
    <html>
    <head>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <div id="calculator"></div>
        <script src="script.js"></script>
    </body>
    </html>
    ```

    ## File: styles.css
    ```css
    body {
        font-family: Arial, sans-serif;
    }
    #calculator {
        margin: 50px auto;
    }
    ```

    ## File: script.js
    ```javascript
    function calculate() {
        // Calculator logic
    }
    document.addEventListener('DOMContentLoaded', calculate);
    ```
    """
        elif 'python' in file_types:
            file_examples = """
    ## File: main.py
    ```python
    from models import Calculator
    from utils import validate_input

    def main():
        calc = Calculator()
        # Main logic
    ```

    ## File: models.py
    ```python
    class Calculator:
        def __init__(self):
            pass
    ```

    ## File: utils.py
    ```python
    def validate_input(value):
        # Validation logic
        pass
    ```
    """
        else:
            file_examples = """
    ## File: filename1.ext
    ```language
    [code for file 1]
    ```

    ## File: filename2.ext
    ```language
    [code for file 2]
    ```

    ## File: filename3.ext
    ```language
    [code for file 3]
    ```
    """
        
        instruction = f"""
    {'='*80}
    {emphasis}: MULTIPLE SEPARATE FILES
    {'='*80}

    USER EXPLICITLY REQUESTED: {detection_result['reason']}

    {you_must} generate SEPARATE files. This is NOT optional.

    Required file types: {', '.join(file_types) if file_types else 'separate modules'}

    MANDATORY FORMAT for each file:
    {file_examples}

    IMPORTANT RULES:
    1. Each file MUST start with "## File: filename.ext"
    2. Each file MUST be in a separate code block with ``` markers
    3. DO NOT combine files into one
    4. DO NOT embed CSS/JS inside HTML
    5. DO NOT create a single monolithic file

    {you_must} generate ALL necessary separate files now.
    {'='*80}
    """
        
        # リトライ時の追加警告
        if attempt > 0:
            instruction += f"""

    ⚠ ATTENTION: This is retry attempt #{attempt + 1}
    The previous attempt failed to generate multiple files.
    You MUST follow the format exactly as shown above.

    """
        
        return instruction

    def _build_system_prompt_enforcement(self, attempt: int, reason: str) -> str:
        """
        System Promptの強制指示を構築
        
        Args:
            attempt: リトライ回数
            reason: 複数ファイル必須の理由
            
        Returns:
            System Prompt追加文字列
        """
        if attempt == 0:
            return f"\n\nCRITICAL: User requested separate files. Generate multiple files using ## File: format. Reason: {reason}"
        elif attempt == 1:
            return f"\n\n⚠ CRITICAL - SECOND ATTEMPT: You FAILED to generate multiple files in the previous attempt. User EXPLICITLY requested separate files. You MUST use ## File: format. Reason: {reason}"
        else:
            return f"\n\n🚨 FINAL CRITICAL WARNING: This is your LAST ATTEMPT. You have FAILED {attempt} times to generate multiple files. User DEMANDS separate files. USE ## File: format NOW. Reason: {reason}"

    def _clean_generated_code(self, code_content: str) -> str:
        """
        Remove all markdown formatting from generated code
        ⭐ 改善版: shebang対応、厳格なMarkdownヘッダー除外
        
        対応するマークダウン形式:
        - ```language ... ```
        - ## File: filename.ext
        - その他のマークダウンヘッダー(###, ####など)
        """
        import re
        
        lines = code_content.split('\n')
        cleaned_lines = []
        in_code_block = False
        skip_next_empty = False
        
        for i, line in enumerate(lines):
            stripped = line.strip()
            
            # ⭐ shebang(#!/usr/bin/env python)は必ず保持
            if stripped.startswith('#!'):
                cleaned_lines.append(line)
                continue
            
            # "## File:" 形式のヘッダーをスキップ
            if stripped.startswith('## File:'):
                skip_next_empty = True
                continue
            
            # ⭐ Markdownヘッダー全体を除外(##, ###, #### すべて)
            if re.match(r'^#{2,6}\s', stripped):
                # ##, ###, ####, #####, ###### を除外
                skip_next_empty = True
                continue
            
            # コードブロック開始/終了マーカー
            if stripped.startswith('```'):
                in_code_block = not in_code_block
                skip_next_empty = True
                continue
            
            # ヘッダー直後の空行をスキップ
            if skip_next_empty and stripped == '':
                skip_next_empty = False
                continue
            
            skip_next_empty = False
            
            # コードブロック内、または通常のコード行を保持
            if in_code_block or not stripped.startswith('#'):
                cleaned_lines.append(line)
            # ⭐ Pythonコメント(# で始まり、次が# でない)は保持
            elif stripped.startswith('#') and len(stripped) > 1:
                if stripped[1] not in ['#', '!']:  # ## や #! 以外
                    cleaned_lines.append(line)
        
        # 前後の空白を除去
        result = '\n'.join(cleaned_lines).strip()
        
        # 最終確認: まだマークダウンが残っているか
        if '```' in result or result.startswith('## File:'):
            # より積極的な除去
            result = self._aggressive_markdown_removal(result)

        # ⭐ 追加クリーニング: より積極的なマークダウン除去
        # コードブロック開始マーカーの完全除去
        result = re.sub(r'^```[a-zA-Z0-9]*\s*$', '', result, flags=re.MULTILINE)
        
        # ## File: 行の完全除去 (残っている場合)
        result = re.sub(r'^##\s*File:\s*[^\n]+\s*$', '', result, flags=re.MULTILINE)
        
        # 連続する空行を1つにまとめる
        result = re.sub(r'\n\n\n+', '\n\n', result)
        
        # 再度前後の空白を除去
        result = result.strip()
        
        return result

    def _extract_multiple_files_from_response(self, response_content: str) -> Dict[str, str]:
        """
        LLMレスポンスから複数ファイルを抽出(強化版)
        
        対応フォーマット:
        1. ## File: filename.ext (⭐ 位置ベース抽出 - ネストされたコードブロック対応)
        2. ### filename.ext
        3. **filename.ext**
        4. filename.ext (ファイル名のみの行 + コードブロック)
        5. File: filename.ext (新規追加)
        6. 📄 filename (新規追加)
        """
        import re
        files = {}
        
        # ==========================================
        # パターン1: ## File: filename (⭐ 対策A: 位置ベース抽出)
        # ==========================================
        # 問題: (.*?)```は非貪欲なので、ネストされた```で終了してしまう
        # 解決: ファイルヘッダーの位置で区切り、セクション内の最初〜最後の```を取得
        
        # 1. すべての "## File:" ヘッダーの位置を特定
        file_headers = list(re.finditer(r'##\s*File:\s*([^\n]+)', response_content, re.IGNORECASE))
        
        for i, match in enumerate(file_headers):
            filename = match.group(1).strip()
            start_pos = match.end()
            
            # 2. 次のファイルヘッダーまで、または文字列の終端まで
            if i + 1 < len(file_headers):
                end_pos = file_headers[i + 1].start()
            else:
                end_pos = len(response_content)
            
            # 3. このセクション内でコードを抽出
            section = response_content[start_pos:end_pos]
            
            # 4. 最初の ``` の開始位置を見つける
            first_fence_match = re.search(r'```(?:[a-zA-Z0-9]+)?\s*\n', section)
            if not first_fence_match:
                continue
            
            code_start = first_fence_match.end()
            
            # 5. 最後の ``` の位置を見つける（セクション内で逆方向検索）
            last_fence_pos = section.rfind('\n```')
            if last_fence_pos == -1:
                last_fence_pos = section.rfind('```')
            
            if last_fence_pos <= code_start:
                continue
            
            # 6. コードを抽出（最初の```の後から最後の```の前まで）
            code = section[code_start:last_fence_pos].strip()
            
            if code and len(code) >= 10:
                files[filename] = code
                logger.debug(f"[Extract:Pattern1-SectionBased] Extracted {filename}: {len(code)} chars")
        
        # ==========================================
        # パターン2: ### filename (マークダウンヘッダー)
        # ==========================================
        pattern2 = r'###\s*([^\n]+\.(?:py|js|html|css|java|cpp|rs|go|rb|php|sh))\n```(?:\w+)?\n(.*?)```'
        matches2 = re.finditer(pattern2, response_content, re.DOTALL | re.IGNORECASE)
        
        for match in matches2:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
        
        # ==========================================
        # パターン3: **filename** (太字)
        # ==========================================
        pattern3 = r'\*\*([^\*]+\.(?:py|js|html|css|java|cpp|rs|go|rb|php|sh))\*\*\n```(?:\w+)?\n(.*?)```'
        matches3 = re.finditer(pattern3, response_content, re.DOTALL | re.IGNORECASE)
        
        for match in matches3:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
        
        # ==========================================
        # パターン4: ファイル名のみの行 + コードブロック
        # ==========================================
        pattern4 = r'^([a-zA-Z0-9_\-\.\/]+\.(?:py|js|html|css|java|cpp|rs|go|rb|php|sh))$\n```(?:\w+)?\n(.*?)```'
        matches4 = re.finditer(pattern4, response_content, re.MULTILINE | re.DOTALL)
        
        for match in matches4:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
        
        # ==========================================
        # パターン5: File: filename (シンプル形式) - 新規追加
        # ==========================================
        pattern5 = r'File:\s*([^\n]+)\n```(?:\w+)?\n(.*?)```'
        matches5 = re.finditer(pattern5, response_content, re.DOTALL | re.IGNORECASE)
        
        for match in matches5:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
        
        # ==========================================
        # パターン6: 📄 filename (絵文字形式) - 新規追加
        # ==========================================
        pattern6 = r'📄\s*([^\n]+)\s*\n[-=]+\s*\n```(?:\w+)?\n(.*?)```'
        matches6 = re.finditer(pattern6, response_content, re.DOTALL)
        
        for match in matches6:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
        
        # ==========================================
        # 【新規追加】パターン7: コメント形式のファイル名（対策2）
        # ==========================================
        # // src/App.js または /* src/App.js */
        pattern7 = r'(?://|/\*)\s*([^\n]+\.(?:js|jsx|ts|tsx|py|html|css|json))\s*(?:\*/)?\s*\n```(?:\w+)?\n(.*?)```'
        matches7 = re.finditer(pattern7, response_content, re.DOTALL)
        
        for match in matches7:
            filename = match.group(1).strip()
            code = match.group(2).strip()
            if filename not in files:
                files[filename] = code
                logger.debug(f"[Extract:Pattern7] Found file with comment marker: {filename}")
        
        # ==========================================
        # 【新規追加】パターン8: インテリジェントフォールバック（対策2）
        # Reactコンポーネント自動検出
        # ==========================================
        if not files:
            logger.debug("[Extract:Fallback] No files found with standard patterns, trying intelligent fallback...")
            
            # すべてのコードブロックを抽出
            code_blocks = re.findall(r'```(?:\w+)?\n(.*?)```', response_content, re.DOTALL)
            
            for i, block in enumerate(code_blocks):
                block_stripped = block.strip()
                
                # Reactコンポーネントの検出
                # 🆕 no_framework_modeの場合はスキップ
                if not getattr(self, '_no_framework_mode', False):
                    if 'export default' in block_stripped and ('function' in block_stripped or 'const' in block_stripped):
                        # function App() または const App = () の検出
                        component_match = re.search(r'(?:function|const)\s+(\w+)', block_stripped)
                        if component_match:
                            component_name = component_match.group(1)
                            filename = f'src/{component_name}.js'
                            files[filename] = block_stripped
                            logger.debug(f"[Extract:Fallback] Auto-detected React component: {filename}")
                            continue
                
                # package.jsonの検出
                if '"name"' in block_stripped and '"version"' in block_stripped and '"dependencies"' in block_stripped:
                    files['package.json'] = block_stripped
                    logger.debug("[Extract:Fallback] Auto-detected package.json")
                    continue
                
                # HTMLファイルの検出
                if '<!DOCTYPE html>' in block_stripped or '<html' in block_stripped:
                    files['public/index.html'] = block_stripped
                    logger.debug("[Extract:Fallback] Auto-detected HTML file")
                    continue
        
        # ==========================================
        # Aiderスタイル抽出(既存のフォールバック)
        # ==========================================
        if not files and self.extractor:
            try:
                modifications = self.extractor.extract_modifications(response_content)
                for mod in modifications:
                    if mod['type'] == 'create':
                        filename = mod['filename']
                        if filename not in files:
                            files[filename] = mod['content']
            except Exception:
                # エラー時は無視
                pass
        
        # ==========================================
        # コード品質チェック: 空ファイル・無効ファイル名を除外
        # ==========================================
        # Windowsで使用できない文字: * : ? " < > |
        # また、Markdown記法の誤検出（**xxx**）も除外
        INVALID_FILENAME_CHARS = set('*:?"<>|')
        
        cleaned_files = {}
        for filename, content in files.items():
            # 最低限のコード量チェック(10文字以上)
            if len(content.strip()) < 10:
                continue
            
            # ファイル名バリデーション: 無効な文字を含むファイル名を除外
            if any(char in filename for char in INVALID_FILENAME_CHARS):
                logger.debug(f"[Extract:Validation] Skipping invalid filename (contains forbidden chars): {filename}")
                continue
            
            # Markdown太字記法の誤検出を除外（**xxx** パターン）
            if filename.startswith('**') or filename.endswith('**'):
                logger.debug(f"[Extract:Validation] Skipping markdown bold pattern: {filename}")
                continue
            
            # ファイル名が空、または空白のみの場合を除外
            if not filename.strip():
                logger.debug("[Extract:Validation] Skipping empty filename")
                continue
            
            cleaned_files[filename] = content
        
        # ==========================================
        # Phase 1: 外部Linter統合
        # ==========================================
        if self.linter_integration and cleaned_files:
            try:
                logger.debug("[Linter] Running external linters...")
                lint_result = self.linter_integration.lint_generated_code(cleaned_files)
                
                if lint_result['has_errors']:
                    # Phase 5: Linter ErrorのTable化（logger版）
                    # 注: 重複表示を避けるため、_clean_and_validate_files側で表示
                    error_count = len(lint_result['errors'])
                    # logger.warning(f"Linter detected {error_count} error(s)") - 削除
                    
                    # Tableは_clean_and_validate_files側で表示されるため、ここでは表示しない
                    # （重複を避けるため）
                    
                    # 自動修正可能かチェック
                    if lint_result.get('can_auto_fix', False):
                        # logger.normal("⚙ Linter errors detected - these may need manual review") - 削除
                        # 注: 自動修正機能は将来実装予定
                        pass  # ifブロックが空にならないようにpass文を追加
                    else:
                        logger.warning("⚠ Critical linter errors - manual review required")
                
                elif lint_result.get('has_warnings', False):
                    logger.debug(f"[Linter] {len(lint_result['warnings'])} warning(s) detected")
                
                else:
                    logger.debug("[Linter] No issues detected")
                
                # Linter使用状況をログ
                if lint_result.get('linters_used'):
                    logger.debug(f"[Linter] Used: {', '.join(lint_result['linters_used'])}")
            
            except Exception as e:
                logger.debug(f"[Linter] Error during lint execution: {e}")
                # Linterエラーは警告のみで処理を継続
        
        return cleaned_files

    def _extract_code_blocks(self, response: str) -> Dict[str, str]:
        """
        LLMのレスポンスからコードブロックを抽出
        
        ⭐ 修正版:
        1. _extract_multiple_files_from_response を使用して複数パターンに対応
        2. _clean_generated_code を呼び出してマークダウン構文を除去
        3. コード品質チェックを強化
        
        Args:
            response: LLMのレスポンステキスト
        
        Returns:
            {filename: code} の辞書
        """
        import re
        
        logger.debug(f"[_extract_code_blocks] Input response length: {len(response)}")
        
        # ==========================================
        # ステップ1: 高機能抽出関数を使用
        # ==========================================
        files = self._extract_multiple_files_from_response(response)  # ⭐ 変更: 6種類のパターンに対応
        
        logger.debug(f"[_extract_code_blocks] Step1: _extract_multiple_files_from_response returned {len(files)} file(s)")
        if files:
            for fn, code in files.items():
                logger.debug(f"[_extract_code_blocks]   Raw: {fn} = {len(code)} chars")
        
        if files:
            # 各ファイルのコードをクリーニング
            cleaned_files = {}
            for filename, code in files.items():
                before_clean = len(code)
                
                # ⭐ 修正: Markdownファイル(.md)はクリーニングをスキップ
                # README.mdなどのMarkdownファイルには##ヘッダーや```コードブロックが必要
                if filename.endswith('.md'):
                    cleaned_code = code  # Markdownはそのまま保持
                    logger.debug(f"[_extract_code_blocks]   Skipping clean for Markdown: {filename}")
                else:
                    cleaned_code = self._clean_generated_code(code)
                after_clean = len(cleaned_code)
                
                # ⭐ 改善4: マーカー残存チェック（Markdown以外のみ）
                if not filename.endswith('.md'):
                    cleaned_code = self._verify_and_fix_markdown_markers(cleaned_code, filename)
                after_verify = len(cleaned_code)
                
                logger.debug(f"[_extract_code_blocks]   Cleaning {filename}: {before_clean} -> {after_clean} -> {after_verify} chars")
                
                # コード品質チェック: 空でないか確認
                if cleaned_code.strip():
                    cleaned_files[filename] = cleaned_code
                else:
                    # 空の場合は警告を出力
                    logger.warning(f"[_extract_code_blocks] Empty code after cleaning for file: {filename}")
                    logger.warning(f"[_extract_code_blocks]   Original code preview: {code[:200]}...")
            
            logger.debug(f"[_extract_code_blocks] Step1 result: {len(cleaned_files)} file(s) after cleaning")
            return cleaned_files
        
        # ==========================================
        # ステップ2: フォールバック - 単一コードブロック
        # ==========================================
        logger.debug(f"[_extract_code_blocks] Step2: Trying fallback (single code block)")
        code_block_pattern = r'```(?:[a-zA-Z0-9]+)?\s*\n(.*?)```'  # ⭐ 改善: [a-z] → [a-zA-Z0-9]
        code_matches = re.findall(code_block_pattern, response, re.DOTALL)
        
        logger.debug(f"[_extract_code_blocks] Step2: Found {len(code_matches)} code block(s)")
        
        if code_matches:
            # 最初のコードブロックをクリーニング
            before_clean = len(code_matches[0])
            cleaned_code = self._clean_generated_code(code_matches[0])  # ⭐ 追加: ここでもクリーニング
            logger.debug(f"[_extract_code_blocks] Step2: Cleaned {before_clean} -> {len(cleaned_code)} chars")
            
            if cleaned_code.strip():
                logger.debug(f"[_extract_code_blocks] Step2: Returning __code__ with {len(cleaned_code)} chars")
                return {'__code__': cleaned_code.strip()}
            else:
                logger.debug(f"[_extract_code_blocks] Step2: Code became empty after cleaning")
        
        # ==========================================
        # ステップ3: コードブロックがない場合
        # ==========================================
        logger.debug("[_extract_code_blocks] Step3: No code blocks found, returning empty dict")
        logger.debug(f"[_extract_code_blocks] Response preview (first 500 chars): {response[:500]}")
        return {}

    def _aggressive_markdown_removal(self, code_content: str) -> str:
        """
        Aggressive markdown removal as fallback
        最後の手段として使用
        """
        lines = code_content.split('\n')
        cleaned = []
        
        for line in lines:
            # マークダウン関連の行を完全除外
            if any([
                line.strip().startswith('```'),
                line.strip().startswith('## File:'),
                line.strip().startswith('## '),
                line.strip() == '##'
            ]):
                continue
            cleaned.append(line)
        
        return '\n'.join(cleaned).strip()

    def _verify_and_fix_markdown_markers(self, code_content: str, filename: str) -> str:
        """
        クリーニング後にマークダウンマーカーが残っていないかを検証し、
        残っている場合は除去する（改善4: マーカー残存チェック）
        
        Args:
            code_content: クリーニング済みのコード
            filename: ファイル名（ログ用）
        
        Returns:
            マーカーが完全に除去されたコード
        """
        import re
        
        # マーカー残存パターン
        marker_patterns = [
            r'^## File:.*$',           # ## File: header
            r'^```[a-zA-Z0-9]*\s*$',   # ``` or ```language
            r'^```$',                   # closing ```
        ]
        
        lines = code_content.split('\n')
        has_markers = False
        
        # 先頭5行をチェック（マーカーは通常先頭にある）
        for i, line in enumerate(lines[:5]):
            for pattern in marker_patterns:
                if re.match(pattern, line.strip()):
                    has_markers = True
                    logger.warning(f"[Marker Check] Found residual marker in {filename} line {i+1}: {line.strip()[:50]}")
                    break
            if has_markers:
                break
        
        if has_markers:
            # マーカーが残っている場合、積極的な除去を実行
            logger.debug(f"[Marker Check] Applying aggressive removal for: {filename}")
            code_content = self._aggressive_markdown_removal(code_content)
            
            # 再チェック: まだ残っている場合は行単位で除去
            lines = code_content.split('\n')
            cleaned_lines = []
            for line in lines:
                skip = False
                for pattern in marker_patterns:
                    if re.match(pattern, line.strip()):
                        skip = True
                        break
                if not skip:
                    cleaned_lines.append(line)
            
            code_content = '\n'.join(cleaned_lines).strip()
        
        return code_content

    # ==========================================
    # 対策C: エントリーポイントのインポートテスト
    # ==========================================
    def _validate_runtime_import(
        self,
        generated_files: Dict[str, str]
    ) -> Tuple[bool, Optional[str], Optional[str]]:
        """
        エントリーポイントのインポートテストを実行
        
        生成されたコードを一時ディレクトリに展開し、エントリーポイント
        (main.py, backend/main.py等)をインポートして実行時エラーを検出する。
        これにより、静的解析では検出できないクラス定義時のエラーなどを
        事前に発見できる。
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
            
        Returns:
            Tuple[bool, Optional[str], Optional[str]]:
                - success: インポート成功かどうか
                - error_message: エラーメッセージ（成功時はNone）
                - failing_file: エラーが発生したファイル（成功時はNone）
        """
        import tempfile
        import importlib.util
        import traceback
        
        # エントリーポイントを特定（優先度順）
        ENTRY_POINT_CANDIDATES = [
            # ルートレベル（優先度高）
            'main.py', 'app.py', 'server.py', 'run.py',
            # サブディレクトリ（FastAPI/Django等）
            'backend/main.py', 'backend/app.py',
            'src/main.py', 'src/app.py',
            'app/main.py', 'app/app.py',
            'api/main.py', 'api/app.py',
            # 深いネスト
            'src/backend/main.py', 'src/api/main.py',
        ]
        
        entry_point = None
        for candidate in ENTRY_POINT_CANDIDATES:
            if candidate in generated_files:
                entry_point = candidate
                break
        
        if not entry_point:
            logger.debug(f"[Runtime Import] No entry point found in {len(generated_files)} files")
            logger.debug(f"[Runtime Import] Searched patterns: {ENTRY_POINT_CANDIDATES[:4]}...")
            return (True, None, None)  # エントリーポイントなし = スキップ
        
        logger.debug(f"[Runtime Import] Testing entry point: {entry_point}")
        
        # 一時ディレクトリに全ファイルを展開
        with tempfile.TemporaryDirectory() as tmpdir:
            try:
                # ファイルを展開
                for filepath, content in generated_files.items():
                    full_path = os.path.join(tmpdir, filepath)
                    dir_path = os.path.dirname(full_path)
                    
                    if dir_path:
                        os.makedirs(dir_path, exist_ok=True)
                    
                    with open(full_path, 'w', encoding='utf-8') as f:
                        f.write(content)
                    
                    # __init__.pyが必要なディレクトリには自動生成
                    if '/' in filepath:
                        parts = filepath.split('/')
                        for i in range(len(parts) - 1):
                            init_dir = os.path.join(tmpdir, *parts[:i+1])
                            init_file = os.path.join(init_dir, '__init__.py')
                            if os.path.isdir(init_dir) and not os.path.exists(init_file):
                                with open(init_file, 'w', encoding='utf-8') as f:
                                    f.write('# Auto-generated __init__.py\n')
                
                # sys.pathに一時ディレクトリを追加
                original_sys_path = sys.path.copy()
                sys.path.insert(0, tmpdir)
                
                # 既存モジュールのキャッシュをクリア（汚染防止）
                modules_to_remove = [m for m in sys.modules.keys() 
                                    if m.startswith(('config', 'models', 'views', 'schemas', 'database',
                                                     'backend', 'src', 'app', 'api', 'routes', 'services'))]
                for m in modules_to_remove:
                    del sys.modules[m]
                
                try:
                    # インポートテスト
                    # backend/main.py -> backend.main (ドット区切りに変換)
                    module_name = entry_point.replace('.py', '').replace('/', '.')
                    spec = importlib.util.spec_from_file_location(
                        module_name,
                        os.path.join(tmpdir, entry_point)
                    )
                    
                    if spec and spec.loader:
                        module = importlib.util.module_from_spec(spec)
                        spec.loader.exec_module(module)
                        
                        logger.debug(f"[Runtime Import] ✅ Successfully imported {entry_point}")
                        return (True, None, None)
                    else:
                        return (False, f"Could not create module spec for {entry_point}", entry_point)
                
                except ModuleNotFoundError as e:
                    # ⭐ 対策A: 外部パッケージ不足はコードのバグではないのでスキップ
                    # LLMで修正することは不可能であり、ユーザー環境でpip install後に正常動作する
                    module_name = str(e).replace("No module named ", "").strip("'\"")
                    logger.debug(f"[Runtime Import] ⚠ External package not found: {module_name}")
                    logger.debug(f"[Runtime Import] This is not a code bug. Skipping test.")
                    # ユーザーへのガイダンスを返す（エラーではなく情報として）
                    return (True, f"EXTERNAL_PACKAGE:{module_name}", None)
                        
                except Exception as e:
                    # エラー発生 - 詳細情報を抽出
                    tb = traceback.format_exc()
                    error_message = str(e)
                    
                    # エラーが発生したファイルを特定
                    failing_file = self._extract_failing_file_from_traceback(tb, tmpdir, generated_files)
                    
                    logger.debug(f"[Runtime Import] ❌ Import failed: {error_message}")
                    logger.debug(f"[Runtime Import]    Failing file: {failing_file}")
                    logger.debug(f"[Runtime Import]    Traceback:\n{tb}")
                    
                    return (False, error_message, failing_file)
                    
                finally:
                    # sys.pathを復元
                    sys.path = original_sys_path
                    
                    # インポートしたモジュールをクリーンアップ
                    modules_to_remove = [m for m in sys.modules.keys() 
                                        if m.startswith(('config', 'models', 'views', 'schemas', 'database',
                                                         'backend', 'src', 'app', 'api', 'routes', 'services'))]
                    for m in modules_to_remove:
                        if m in sys.modules:
                            del sys.modules[m]
                            
            except Exception as e:
                logger.debug(f"[Runtime Import] Error during test setup: {e}")
                return (False, str(e), None)
    
    def _extract_failing_file_from_traceback(
        self,
        tb: str,
        tmpdir: str,
        generated_files: Dict[str, str]
    ) -> Optional[str]:
        """
        トレースバックからエラーが発生したファイルを特定
        
        Args:
            tb: トレースバック文字列
            tmpdir: 一時ディレクトリパス
            generated_files: 生成されたファイル
            
        Returns:
            エラーが発生したファイルパス（特定できない場合はNone）
        """
        import re
        
        # トレースバックからファイルパスを抽出
        # パターン: File "path/to/file.py", line X
        pattern = r'File "([^"]+)", line (\d+)'
        matches = re.findall(pattern, tb)
        
        for filepath, line_no in reversed(matches):  # 最後（最も深い）から検索
            # 一時ディレクトリのパスを除去
            if tmpdir in filepath:
                relative_path = filepath.replace(tmpdir + os.sep, '').replace(tmpdir + '/', '')
                # Windowsパス対応
                relative_path = relative_path.replace('\\', '/')
                
                if relative_path in generated_files:
                    return relative_path
        
        return None
    
    def _fix_import_error_with_llm(
        self,
        generated_files: Dict[str, str],
        error_message: str,
        failing_file: Optional[str]
    ) -> Optional[Dict[str, str]]:
        """
        LLMを使用してインポートエラーを自動修正
        
        Args:
            generated_files: 生成されたファイル
            error_message: エラーメッセージ
            failing_file: エラーが発生したファイル
            
        Returns:
            修正されたファイル（修正できない場合はNone）
        """
        if not failing_file or failing_file not in generated_files:
            logger.debug(f"[Import Fix] Cannot fix: failing_file not identified or not in generated_files")
            return None
        
        logger.debug(f"[Import Fix] Attempting to fix: {failing_file}")
        
        # エラーが発生したファイルのコード
        failing_code = generated_files[failing_file]
        
        # 関連ファイル（インポート先など）のコンテキスト
        related_files_context = []
        for filepath, content in generated_files.items():
            if filepath != failing_file and filepath.endswith('.py'):
                # 最大3ファイルまで
                if len(related_files_context) < 3:
                    related_files_context.append(f"## File: {filepath}\n```python\n{content}\n```")
        
        related_context = "\n\n".join(related_files_context) if related_files_context else "No related files"
        
        # 修正プロンプト
        # Note: コードフェンスを変数化してf-string内での問題を回避
        code_fence = "```"
        prompt = f"""Fix the Python import/runtime error in the following code.

## Error Information
- Error Message: {error_message}
- Failing File: {failing_file}

## Common Causes
1. Class-level code that executes at import time (e.g., validation in class body)
2. Missing __init__.py files
3. Circular imports
4. Undefined variables at module level
5. Missing dependencies

## CRITICAL FIX PATTERNS

### Pattern 1: Class-level validation (WRONG)
{code_fence}python
class SomeConfig(BaseConfig):
    SECRET_KEY = os.environ.get('SECRET_KEY')
    if not SECRET_KEY:  # This executes at import time!
        raise ValueError("...")
{code_fence}

### Pattern 1: Class-level validation (CORRECT)
{code_fence}python
class SomeConfig(BaseConfig):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'default-dev-key'
    # Or use @property for runtime validation
{code_fence}

### Pattern 2: Missing fallback (WRONG)
{code_fence}python
DATABASE_URL = os.environ['DATABASE_URL']  # Raises KeyError if not set
{code_fence}

### Pattern 2: Missing fallback (CORRECT)
{code_fence}python
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
{code_fence}

## File with Error: {failing_file}
{code_fence}python
{failing_code}
{code_fence}

## Related Files (for context)
{related_context}

## Instructions
1. Identify the exact cause of the import error
2. Fix the issue while preserving all functionality
3. Return the COMPLETE fixed file

Return ONLY the fixed code in this format:
## File: {failing_file}
{code_fence}python
<complete fixed code here>
{code_fence}
"""
        
        try:
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a Python expert. Fix the import/runtime error. Return only the fixed code.",
                max_tokens=8000
            )
            
            if hasattr(response, 'content'):
                response_text = response.content
            elif isinstance(response, str):
                response_text = response
            else:
                logger.debug("[Import Fix] Unexpected response type")
                return None
            
            # 修正されたコードを抽出
            fixed_files = self._extract_code_blocks(response_text)
            
            if fixed_files:
                # 修正されたファイルで更新
                result = generated_files.copy()
                for filename, code in fixed_files.items():
                    # ファイル名の正規化
                    normalized_name = filename.replace('\\', '/')
                    if normalized_name == failing_file or filename == failing_file:
                        result[failing_file] = code
                        logger.debug(f"[Import Fix] ✅ Fixed {failing_file} ({len(code)} chars)")
                        return result
                
                # ファイル名が一致しない場合、最初のファイルを使用
                first_code = list(fixed_files.values())[0]
                result[failing_file] = first_code
                logger.debug(f"[Import Fix] ✅ Fixed {failing_file} (name mismatch, used first file)")
                return result
            
            logger.debug("[Import Fix] No code blocks extracted from LLM response")
            return None
            
        except Exception as e:
            logger.debug(f"[Import Fix] Error during LLM fix: {e}")
            return None

    # 行3258-3263: 関数定義（インデント: 4スペース）- 変更なし
    def _fix_linter_errors_with_llm(
        self,
        generated_files: Dict[str, str],
        errors: List[Dict[str, Any]],
        file_languages: Dict[str, str] = None
    ) -> Optional[Dict[str, str]]:
        """
        LLMを使用してLintエラーを自動修正
        
        Args:
            generated_files: 生成されたコード
            errors: Lintエラーのリスト
            file_languages: ファイル別言語情報
            
        Returns:
            修正されたコード
        """
        # 修正: 2025-12-29
        # 問題: エラー数が多すぎる（187件等）とプロンプトが巨大化し、
        #       LLMの出力が途切れたり、抽出パターンにマッチしなくなる
        # 対策: 
        #   1. ファイルごとに最大20件のエラーを選択
        #   2. 合計で最大60件に制限
        #   3. 重要度順にソート（Critical > High > Medium > Low）
        
        MAX_ERRORS_PER_FILE = 20
        MAX_TOTAL_ERRORS = 60
        
        # 重要度でソート
        severity_order = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3, 'warning': 4}
        
        def get_severity(err):
            severity = err.get('severity', 'low').lower()
            return severity_order.get(severity, 3)
        
        sorted_errors = sorted(errors, key=get_severity)
        
        # ファイルごとにエラーをグループ化し、制限
        from collections import defaultdict
        errors_by_file = defaultdict(list)
        for err in sorted_errors:
            # 🆕 存在しないファイルへのエラーを除外（tscが未生成ファイルを参照する問題対策）
            if err['file'] not in generated_files:
                logger.debug(f"[LLM Fix] Skipping error for non-existent file: {err['file']}")
                continue
            errors_by_file[err['file']].append(err)
        
        # エラーが全て除外された場合は修正不要
        if not errors_by_file:
            logger.debug("[LLM Fix] All errors refer to non-existent files, skipping fix")
            return None
        
        limited_errors = []
        for filename, file_errors in errors_by_file.items():
            limited_errors.extend(file_errors[:MAX_ERRORS_PER_FILE])
        
        # 合計制限
        limited_errors = limited_errors[:MAX_TOTAL_ERRORS]
        
        if len(errors) > len(limited_errors):
            logger.debug(f"[LLM Fix] Limited errors from {len(errors)} to {len(limited_errors)} (max {MAX_TOTAL_ERRORS})")
        
        # エラー情報を整形（詳細版 - 行番号、エラーコード、メッセージを含む）
        error_details = []
        for i, err in enumerate(limited_errors, 1):
            error_info = [
                f"Error #{i}:",
                f"  File: {err['file']}",
                f"  Line: {err.get('line', 'N/A')}, Column: {err.get('column', 'N/A')}",
                f"  Code: {err.get('code', 'N/A')}",
                f"  Message: {err.get('message', 'No message')}",
                f"  Linter: {err.get('linter', 'unknown')}",
            ]
            error_details.append('\n'.join(error_info))
        
        error_text = '\n\n'.join(error_details)
        

            
        # エラーが発生したファイルのコードを抽出
        error_files = set(err['file'] for err in errors)
        files_context = []
        for filename in error_files:
            if filename in generated_files:
                code = generated_files[filename]
                files_context.append(f"## File: {filename}\n```\n{code}\n```")
            
        files_text = "\n\n".join(files_context)
        
        # リポジトリマップを生成（モジュール間の関係性を提供）
        repository_map = self._generate_repository_map()
            
        # 修正プロンプト構築
        # ✅ 言語種別を明示的に指定
        # language_requirementsを生成
        if file_languages:
            lang_reqs = []
            for filename, lang in file_languages.items():
                if lang == 'html':
                    lang_reqs.append(f"- {filename}: HTML5 標準準拠、alt属性必須")
                elif lang == 'css':
                    lang_reqs.append(f"- {filename}: CSS3 標準準拠")
                elif lang == 'javascript':
                    lang_reqs.append(f"- {filename}: Pure JavaScript (ES6+), NO TypeScript")
                elif lang == 'python':
                    lang_reqs.append(f"- {filename}: PEP8 準拠")
                else:
                    lang_reqs.append(f"- {filename}: {lang} best practices")
            language_requirements = "\n    ".join(lang_reqs) if lang_reqs else "No specific requirements"
        else:
            language_requirements = "No specific requirements"
        
        prompt = f"""Fix the following linter errors in the code.

    ⚠️ CRITICAL CONSTRAINTS:
    - Do NOT create any new files. Only modify the existing files listed below.
    - If an error refers to a non-existent file, IGNORE that error entirely.
    - You must ONLY output files that already exist in "CURRENT CODE" section.

    REPOSITORY CONTEXT (available functions and classes):
    {repository_map if repository_map else 'No repository context available'}

    LINTER ERRORS:
    {error_text}

    CURRENT CODE:
    {files_text}

    LANGUAGE-SPECIFIC REQUIREMENTS:
    {language_requirements}  # ✅ 言語別の要件を追加

    INSTRUCTIONS:
    1. Fix ONLY the reported errors at the specified lines and columns
    2. For each error, address the specific issue mentioned in the error message
    3. Do NOT change any other code
    4. Preserve all existing functionality
    5. Keep the code in its original language (JavaScript, Python, etc.)
    6. Do NOT create new files - only fix files that exist in CURRENT CODE above
    7. Return the fixed files using this format:

    ## File: filename
    ```
    fixed code here
    ```

    Generate the fixed code now."""
            
        try:
            logger.debug("[LLM Fix] Requesting error fixes from LLM...")
            
            # 修正: 2025-12-29
            # 問題: max_tokens未指定でデフォルト値（4096）が使用され、
            #       大きなファイルの修正結果が途切れる
            # 対策: max_tokens=16000 を明示的に指定
            #
            # また、エラーファイル数に応じてmax_tokensを調整
            error_file_count = len(set(err['file'] for err in errors))
            if error_file_count >= 3:
                fix_max_tokens = 16000
            elif error_file_count == 2:
                fix_max_tokens = 16000
            else:
                fix_max_tokens = 8000
            
            logger.debug(f"[LLM Fix] Using max_tokens={fix_max_tokens} for {error_file_count} file(s)")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality expert. Fix linter errors without changing functionality.",
                max_tokens=fix_max_tokens
            )
            
            # レスポンスを文字列に変換
            if hasattr(response, 'content'):
                response_text = response.content
            elif isinstance(response, str):
                response_text = response
            else:
                response_text = str(response)
            
            logger.debug(f"[LLM Fix] Response received, length: {len(response_text)}")
            
            # 修正されたコードを抽出
            fixed_files = self._extract_code_blocks(response_text)
            
            if fixed_files:
                # 🆕 新規ファイル除外: 元のファイルセットにないファイルは捨てる
                original_filenames = set(generated_files.keys())
                
                # ファイル名正規化マッピング（LLMが微妙に異なるファイル名を返す対策）
                # 例: "./neon_tetris.html" → "neon_tetris.html"
                normalized_originals = {}
                for orig in original_filenames:
                    # 正規化キー: 先頭の./ を除去 + 小文字化
                    norm_key = orig
                    while norm_key.startswith('./'):
                        norm_key = norm_key[2:]
                    norm_key = norm_key.lower()
                    normalized_originals[norm_key] = orig
                    # basename単体もマッピング（パス不一致対策）
                    base_key = os.path.basename(orig).lower()
                    if base_key not in normalized_originals:
                        normalized_originals[base_key] = orig
                
                filtered_files = {}
                for k, v in fixed_files.items():
                    if k in original_filenames:
                        # 完全一致
                        filtered_files[k] = v
                    else:
                        # 正規化一致を試行
                        norm_k = k
                        while norm_k.startswith('./'):
                            norm_k = norm_k[2:]
                        norm_k = norm_k.lower()
                        base_k = os.path.basename(k).lower()
                        
                        matched_original = None
                        if norm_k in normalized_originals:
                            matched_original = normalized_originals[norm_k]
                        elif base_k in normalized_originals:
                            matched_original = normalized_originals[base_k]
                        
                        if matched_original:
                            # 正規化一致: 元のファイル名をキーとして使用
                            filtered_files[matched_original] = v
                            logger.debug(f"[LLM Fix] Normalized filename: '{k}' -> '{matched_original}'")
                        else:
                            logger.debug(f"[LLM Fix] Removed unexpected new file: '{k}'")
                
                if len(filtered_files) < len(fixed_files):
                    removed_count = len(fixed_files) - len(filtered_files)
                    logger.debug(f"[LLM Fix] Filtered out {removed_count} unexpected file(s)")
                
                fixed_files = filtered_files
                
                if not fixed_files:
                    logger.debug("[LLM Fix] No valid fixes after filtering out new files")
                    return None
                
                # 元のファイルとマージ (修正されなかったファイルも含める)
                result = generated_files.copy()
                result.update(fixed_files)
                
                logger.debug(f"[LLM Fix] Successfully fixed {len(fixed_files)} file(s)")
                return result
            else:
                logger.debug("[LLM Fix] Failed to extract fixed code from LLM response")
                return None
                
        except Exception as e:
            logger.debug(f"[LLM Fix] Error during auto-fix: {e}")
            return None

    # ==========================================
    # Cross-File Consistency Check Methods
    # ==========================================
    
    def _check_cross_file_consistency(
        self, 
        generated_code: Dict[str, str]
    ) -> Dict[str, Any]:
        """
        ファイル間整合性チェック（Phase 1: モデル↔初期データ）
        
        SQLAlchemyモデルの必須フィールドと初期データの整合性を検証し、
        欠落フィールドを検出する。
        
        Args:
            generated_code: ファイル名とコードの辞書
            
        Returns:
            {
                'has_issues': bool,
                'issues': [
                    {
                        'type': 'model_init_data_mismatch',
                        'model': 'Enemy',
                        'tablename': 'enemies',
                        'field': 'max_health',
                        'model_file': 'models/enemy.py',
                        'init_file': 'database.py',
                        'message': "Required field 'max_health' missing in init data"
                    }
                ],
                'affected_files': ['database.py'],
                'model_definitions': {model_name: {field: type_info}}
            }
        """
        logger.debug("[Cross-File] Starting consistency check...")
        
        issues = []
        affected_files = set()
        model_definitions = {}
        
        # Step 1: モデルファイルから必須フィールドを抽出
        all_model_info = []
        for filename, code in generated_code.items():
            if filename.startswith('models/') and filename.endswith('.py'):
                if filename == 'models/__init__.py':
                    continue
                model_info_list = self._extract_model_required_fields(code, filename)
                all_model_info.extend(model_info_list)
                
                # 修正用にモデル定義を保存
                for info in model_info_list:
                    model_definitions[info['model_name']] = {
                        'tablename': info['tablename'],
                        'required_fields': info['required_fields'],
                        'field_definitions': info.get('field_definitions', {}),
                        'source_file': filename
                    }
        
        logger.debug(f"[Cross-File] Found {len(all_model_info)} model(s) with required fields")
        for info in all_model_info:
            logger.debug(f"[Cross-File]   {info['model_name']} ({info['tablename']}): {info['required_fields']}")
        
        # Step 2: database.py から初期データのフィールドを抽出
        init_data_fields = {}
        if 'database.py' in generated_code:
            init_data_fields = self._extract_init_data_fields(generated_code['database.py'])
            logger.debug(f"[Cross-File] Found init data for tables: {list(init_data_fields.keys())}")
            for table, fields in init_data_fields.items():
                logger.debug(f"[Cross-File]   {table}: {fields}")
        else:
            logger.debug("[Cross-File] No database.py found, skipping init data check")
            return {'has_issues': False, 'issues': [], 'affected_files': [], 'model_definitions': {}}
        
        # Step 3: 突合チェック
        for model_info in all_model_info:
            tablename = model_info['tablename']
            model_name = model_info['model_name']
            required_fields = model_info['required_fields']
            model_file = model_info['source_file']
            
            # 初期データが存在するテーブルのみチェック
            if tablename not in init_data_fields:
                logger.debug(f"[Cross-File] No init data for table '{tablename}', skipping")
                continue
            
            provided_fields = init_data_fields[tablename]
            
            for field in required_fields:
                if field not in provided_fields:
                    issue = {
                        'type': 'model_init_data_mismatch',
                        'model': model_name,
                        'tablename': tablename,
                        'field': field,
                        'model_file': model_file,
                        'init_file': 'database.py',
                        'message': f"Required field '{field}' missing in init data for {model_name}"
                    }
                    issues.append(issue)
                    affected_files.add('database.py')
                    logger.debug(f"[Cross-File] ❌ Issue found: {issue['message']}")
        
        has_issues = len(issues) > 0
        
        if has_issues:
            logger.debug(f"[Cross-File] Found {len(issues)} consistency issue(s)")
        else:
            logger.debug("[Cross-File] ✅ No consistency issues found")
        
        return {
            'has_issues': has_issues,
            'issues': issues,
            'affected_files': list(affected_files),
            'model_definitions': model_definitions
        }
    
    def _extract_model_required_fields(
        self, 
        model_code: str, 
        filename: str
    ) -> List[Dict]:
        """
        モデルファイルから必須フィールドを抽出
        
        1ファイル複数モデル対応（classブロック単位で処理）
        
        抽出条件:
        - mapped_column() を含む行
        - nullable=False を含む（明示的のみ）
        - default= を含まない
        - primary_key=True を除外
        - ForeignKey( を除外
        
        Args:
            model_code: モデルファイルのソースコード
            filename: ファイル名（ログ用）
            
        Returns:
            [
                {
                    'model_name': 'Enemy',
                    'tablename': 'enemies',
                    'required_fields': ['name', 'enemy_type', 'health', 'max_health', 'damage'],
                    'field_definitions': {'max_health': 'Mapped[int]', ...},
                    'source_file': 'models/enemy.py'
                },
                ...
            ]
        """
        import re
        
        results = []
        lines = model_code.split('\n')
        
        current_class = None
        current_tablename = None
        current_required_fields = []
        current_field_definitions = {}
        
        # クラス定義パターン
        class_pattern = re.compile(r'^class\s+(\w+)\s*\(.*(?:db\.Model|Base)')
        # テーブル名パターン
        tablename_pattern = re.compile(r"__tablename__\s*=\s*['\"](\w+)['\"]")
        # mapped_columnパターン（複数行対応のため柔軟に）
        mapped_column_pattern = re.compile(
            r'^\s+(\w+):\s*Mapped\[([^\]]+)\]\s*=\s*mapped_column\s*\(([^)]*)\)'
        )
        
        for line in lines:
            # クラス定義を検出
            class_match = class_pattern.match(line)
            if class_match:
                # 前のクラスの結果を保存
                if current_class and current_tablename and current_required_fields:
                    results.append({
                        'model_name': current_class,
                        'tablename': current_tablename,
                        'required_fields': current_required_fields,
                        'field_definitions': current_field_definitions,
                        'source_file': filename
                    })
                
                # 新しいクラスを開始
                current_class = class_match.group(1)
                current_tablename = None
                current_required_fields = []
                current_field_definitions = {}
                continue
            
            # テーブル名を検出
            tablename_match = tablename_pattern.search(line)
            if tablename_match and current_class:
                current_tablename = tablename_match.group(1)
                continue
            
            # mapped_columnを検出
            mc_match = mapped_column_pattern.match(line)
            if mc_match and current_class:
                field_name = mc_match.group(1)
                field_type = mc_match.group(2)
                column_args = mc_match.group(3)
                
                # 除外条件
                if 'primary_key=True' in column_args or 'primary_key' in column_args:
                    continue
                if 'ForeignKey(' in column_args or 'ForeignKey (' in column_args:
                    continue
                
                # default= があればスキップ（オプショナル）
                if 'default=' in column_args or 'default =' in column_args:
                    continue
                
                # nullable=True があればスキップ
                if 'nullable=True' in column_args or 'nullable = True' in column_args:
                    continue
                
                # Optional型はスキップ
                if 'Optional[' in field_type:
                    continue
                
                # nullable=False のみ必須として扱う（明示的指定のみ）
                if 'nullable=False' in column_args or 'nullable = False' in column_args:
                    current_required_fields.append(field_name)
                    current_field_definitions[field_name] = f"Mapped[{field_type}]"
        
        # 最後のクラスの結果を保存
        if current_class and current_tablename and current_required_fields:
            results.append({
                'model_name': current_class,
                'tablename': current_tablename,
                'required_fields': current_required_fields,
                'field_definitions': current_field_definitions,
                'source_file': filename
            })
        
        return results
    
    def _extract_init_data_fields(
        self, 
        database_code: str
    ) -> Dict[str, Set[str]]:
        """
        database.py から初期データのフィールドを抽出
        
        パターン: default_{tablename} = [...]
        
        Args:
            database_code: database.pyのソースコード
            
        Returns:
            {
                'enemies': {'name', 'enemy_type', 'health', 'damage', ...},
                'weapons': {'name', 'weapon_type', 'damage', ...},
            }
        """
        import re
        
        result = {}
        
        # default_xxx = [ パターンを検出
        # 例: default_weapons = [, default_enemies = [
        init_data_pattern = re.compile(r'default_(\w+)\s*=\s*\[')
        
        # 辞書キーパターン（'key': または "key":）
        dict_key_pattern = re.compile(r"['\"](\w+)['\"]\s*:")
        
        lines = database_code.split('\n')
        current_table = None
        bracket_depth = 0
        current_fields = set()
        
        for line in lines:
            # 新しい初期データ変数を検出
            init_match = init_data_pattern.search(line)
            if init_match:
                # 前のテーブルの結果を保存
                if current_table and current_fields:
                    result[current_table] = current_fields
                
                # 変数名からテーブル名を推定（default_enemies -> enemies）
                var_name = init_match.group(1)
                current_table = var_name
                current_fields = set()
                bracket_depth = line.count('[') - line.count(']')
                continue
            
            # 現在のテーブルのデータを収集中
            if current_table:
                bracket_depth += line.count('[') - line.count(']')
                bracket_depth += line.count('{') - line.count('}')
                
                # 辞書キーを抽出
                keys = dict_key_pattern.findall(line)
                current_fields.update(keys)
                
                # データ終了を検出（ブラケット深度が0以下かつフィールドがある）
                if bracket_depth <= 0 and current_fields:
                    result[current_table] = current_fields
                    current_table = None
                    current_fields = set()
                    bracket_depth = 0
        
        # 最後のテーブルの結果を保存
        if current_table and current_fields:
            result[current_table] = current_fields
        
        return result
    
    def _fix_cross_file_issues_with_llm(
        self,
        generated_code: Dict[str, str],
        issues: List[Dict],
        model_definitions: Dict[str, Dict]
    ) -> Optional[Dict[str, str]]:
        """
        Cross-File整合性問題をLLMで修正
        
        Args:
            generated_code: ファイル名とコードの辞書
            issues: 検出された問題のリスト
            model_definitions: モデル定義情報（型情報含む）
            
        Returns:
            修正されたファイルの辞書、失敗時はNone
        """
        if not issues:
            return None
        
        # 影響を受けるファイル（database.py）のコードを取得
        database_code = generated_code.get('database.py', '')
        if not database_code:
            logger.debug("[Cross-File Fix] database.py not found")
            return None
        
        # 問題の詳細を整形
        issue_details = []
        for i, issue in enumerate(issues, 1):
            model_name = issue['model']
            field = issue['field']
            
            # モデル定義から型情報を取得
            field_type = "Unknown"
            if model_name in model_definitions:
                field_defs = model_definitions[model_name].get('field_definitions', {})
                field_type = field_defs.get(field, "Unknown")
            
            issue_details.append(
                f"{i}. Model: {model_name}, Field: {field}, Type: {field_type}\n"
                f"   Missing in: database.py (default_{issue['tablename']} initialization)"
            )
        
        issues_text = "\n".join(issue_details)
        
        # モデル定義の参照情報を整形
        model_refs = []
        for model_name, info in model_definitions.items():
            if any(issue['model'] == model_name for issue in issues):
                fields_info = []
                for field, field_type in info.get('field_definitions', {}).items():
                    fields_info.append(f"  - {field}: {field_type}")
                model_refs.append(
                    f"Model: {model_name} (table: {info['tablename']})\n" +
                    "\n".join(fields_info)
                )
        
        model_refs_text = "\n\n".join(model_refs)
        
        # 修正プロンプト構築
        fix_prompt = f"""You are fixing a cross-file consistency issue in a Flask/SQLAlchemy application.

## Issue Description
The following required fields are missing in the initialization data (database.py):

{issues_text}

## Model Definitions (for reference)
{model_refs_text}

## Current database.py
```python
{database_code}
```

## Task
Fix the database.py file by adding the missing required fields to the initialization data.

IMPORTANT RULES:
1. For each missing field, add an appropriate default value based on the field type and context
2. For 'max_health', use the same value as 'health' (they should match initially)
3. For other integer fields, use sensible defaults (0 or context-appropriate values)
4. Do NOT modify any model files - only fix database.py
5. Maintain the existing code structure and formatting
6. Output ONLY the fixed database.py file

## Output Format
```python
# database.py
[complete fixed code here]
```
"""
        
        try:
            logger.debug("[Cross-File Fix] Sending fix request to LLM...")
            
            response = self._call_llm_api(fix_prompt)
            
            if not response:
                logger.debug("[Cross-File Fix] No response from LLM")
                return None
            
            # レスポンスからコードを抽出
            extracted = self._extract_code_from_response(response, ['database.py'])
            
            if extracted and 'database.py' in extracted:
                fixed_code = extracted['database.py']
                
                # 修正が適用されたか確認（簡易チェック）
                missing_fields_found = 0
                for issue in issues:
                    field = issue['field']
                    if f"'{field}'" in fixed_code or f'"{field}"' in fixed_code:
                        missing_fields_found += 1
                
                if missing_fields_found > 0:
                    logger.debug(f"[Cross-File Fix] ✅ Fixed {missing_fields_found}/{len(issues)} missing fields")
                    return {'database.py': fixed_code}
                else:
                    logger.debug("[Cross-File Fix] ❌ Fix did not add missing fields")
                    return None
            
            logger.debug("[Cross-File Fix] ❌ Could not extract fixed code from response")
            return None
            
        except Exception as e:
            logger.debug(f"[Cross-File Fix] ❌ Error during fix: {e}")
            return None

    def _generate_repository_map(self) -> str:
        """
        Aider方式のリポジトリマップを生成
        
        プロジェクト全体の構造(クラス、メソッド、関数のシグネチャ)を
        LLMが理解しやすい形式で出力します。
        
        Returns:
            str: リポジトリマップ文字列(シグネチャ集)
                各ファイルのクラス・メソッド・関数の定義を含む
        
        Example output:
            cognix/models/user.py:
            class User
                def __init__(self, name, email)
                def validate(self)
            def create_user(username, password)
        
        参考: https://aider.chat/docs/repomap.html
        """
        if not self.repository_analyzer:
                        return ""
        
        map_parts = []
        
        try:
            # repository_analyzer.repository_data から各ファイルの情報を取得
            if not hasattr(self.repository_analyzer, 'memory') or \
               not hasattr(self.repository_analyzer.memory, 'repository_data'):
                                return ""
            
            repository_data = self.repository_analyzer.memory.repository_data
            logger.debug(f"[DEBUG] Processing {len(repository_data)} files")
            
            # 各ファイルのシグネチャを抽出
            files_with_content = 0
            for file_path, repo_file in repository_data.items():
                # ファイルパスを追加
                file_header = f"\n{file_path}:"
                file_content = []
                
                # クラスとそのメソッドを追加
                if hasattr(repo_file, 'classes') and repo_file.classes:
                    logger.debug(f"[DEBUG] {file_path}: {len(repo_file.classes)} classes")
                    for cls in repo_file.classes:
                        cls_name = cls.get('name', 'UnknownClass')
                        file_content.append(f"  class {cls_name}")
                        
                        # クラスのメソッドを追加
                        methods = cls.get('methods', [])
                        logger.debug(f"[DEBUG]  Class {cls_name}: {len(methods)} methods")
                        for method in methods:
                            # データ型を検証(古いデータ構造への対応)
                            if isinstance(method, dict):
                                method_name = method.get('name', 'unknown_method')
                                params = method.get('params', [])
                                params_str = ', '.join(params)
                                file_content.append(f"    def {method_name}({params_str})")
                            elif isinstance(method, str):
                                # 古いデータ構造(文字列のみ)
                                logger.debug(f"[DEBUG]  ⚠ Old format detected: {method}")
                                file_content.append(f"    def {method}()")
                            else:
                                logger.debug(f"[DEBUG]  ⚠ Unknown method format: {type(method)}")
                else:
                    logger.debug(f"[DEBUG] {file_path}: no classes (hasattr={hasattr(repo_file, 'classes')}, value={getattr(repo_file, 'classes', None)})")
                
                # トップレベル関数を追加
                if hasattr(repo_file, 'functions') and repo_file.functions:
                    logger.debug(f"[DEBUG] {file_path}: {len(repo_file.functions)} functions")
                    for func in repo_file.functions:
                        # データ型を検証(古いデータ構造への対応)
                        if isinstance(func, dict):
                            func_name = func.get('name', 'unknown_function')
                            params = func.get('params', [])
                            params_str = ', '.join(params)
                            file_content.append(f"  def {func_name}({params_str})")
                        elif isinstance(func, str):
                            # 古いデータ構造(文字列のみ)
                            logger.debug(f"[DEBUG]  ⚠ Old format detected: {func}")
                            file_content.append(f"  def {func}()")
                        else:
                            logger.debug(f"[DEBUG]  ⚠ Unknown function format: {type(func)}")
                else:
                    logger.debug(f"[DEBUG] {file_path}: no functions (hasattr={hasattr(repo_file, 'functions')}, value={getattr(repo_file, 'functions', None)})")
                
                # HTMLファイルの構造情報を抽出
                if file_path.endswith('.html') or file_path.endswith('.htm'):
                    html_info = self._extract_html_structure(file_path)
                    if html_info:
                        file_content.extend(html_info)
                        logger.debug(f"[DEBUG] {file_path}: extracted HTML structure ({len(html_info)} lines)")
                
                # CSSファイルのクラス情報を抽出
                if file_path.endswith('.css') or file_path.endswith('.scss') or file_path.endswith('.sass'):
                    css_info = self._extract_css_structure(file_path)
                    if css_info:
                        file_content.extend(css_info)
                        logger.debug(f"[DEBUG] {file_path}: extracted CSS structure ({len(css_info)} lines)")
                
                # ========================================
                # Phase 2追加: JavaScriptファイルの構造情報を抽出
                # ========================================
                if file_path.endswith('.js') or file_path.endswith('.jsx'):
                    js_info = self._extract_javascript_interfaces(file_path)
                    if js_info:
                        file_content.extend(js_info)
                        logger.debug(f"[DEBUG] {file_path}: extracted JavaScript structure ({len(js_info)} lines)")
                
                # ファイルに内容がある場合のみ追加
                if file_content:
                    map_parts.append(file_header)
                    map_parts.extend(file_content)
                    files_with_content += 1
            
            logger.debug(f"[DEBUG] Files with content: {files_with_content}/{len(repository_data)}")
            logger.debug(f"[DEBUG] Total map_parts: {len(map_parts)}")
        
        except Exception as e:
            # エラーが発生した場合は空文字列を返す
            logger.debug(f"⚠ Repository map generation failed: {e}")
            import traceback
            traceback.print_exc()
            return ""
        
        result = '\n'.join(map_parts)
        logger.debug(f"[DEBUG] Final map length: {len(result)} chars")
        return result

    def _extract_html_structure(self, file_path: str) -> List[str]:
        """
        HTMLファイルから構造情報を抽出（Phase 2拡張版 - 全data-*属性対応）
        
        Args:
            file_path: HTMLファイルのパス
            
        Returns:
            List[str]: HTML構造情報のリスト
            
        Phase 2拡張内容:
            - 全data-*属性を網羅的に抽出
            - data属性の一覧をサマリー表示
        """
        try:
            from pathlib import Path
            import re
            
            # ファイルを読み込む
            full_path = Path.cwd() / file_path
            if not full_path.exists():
                return []
            
            content = full_path.read_text(encoding='utf-8', errors='ignore')
            structure = []
            
            # ========================================
            # 全data-*属性を収集（Phase 2追加）
            # ========================================
            all_data_attrs = {}  # {attr_name: [values]}
            
            # data-*属性を全て抽出するパターン
            data_attr_pattern = r'data-([a-zA-Z0-9_-]+)=["\']([^"\']*)["\']'
            data_matches = re.findall(data_attr_pattern, content, re.IGNORECASE)
            
            for attr_name, attr_value in data_matches:
                full_attr = f'data-{attr_name}'
                if full_attr not in all_data_attrs:
                    all_data_attrs[full_attr] = []
                if attr_value and attr_value not in all_data_attrs[full_attr]:
                    all_data_attrs[full_attr].append(attr_value)
            
            # ========================================
            # ボタン要素を抽出（既存機能維持）
            # ========================================
            button_pattern = r'<button[^>]*(?:class=["\']([^"\']*)["\'])?[^>]*(?:data-value=["\']([^"\']*)["\'])?[^>]*(?:id=["\']([^"\']*)["\'])?[^>]*>([^<]*)</button>'
            buttons = re.findall(button_pattern, content, re.IGNORECASE)
            if buttons:
                structure.append("  buttons:")
                for btn_class, data_value, btn_id, text in buttons:
                    btn_info = []
                    if text.strip():
                        btn_info.append(f"'{text.strip()}'")
                    if btn_class:
                        btn_info.append(f"class='{btn_class}'")
                    if data_value:
                        btn_info.append(f"value='{data_value}'")
                    if btn_id:
                        btn_info.append(f"id='{btn_id}'")
                    structure.append(f"    - {' '.join(btn_info)}")
            
            # ========================================
            # 入力要素を抽出（既存機能維持）
            # ========================================
            input_pattern = r'<input[^>]*(?:type=["\']([^"\']*)["\'])?[^>]*(?:id=["\']([^"\']*)["\'])?[^>]*(?:class=["\']([^"\']*)["\'])?[^>]*>'
            inputs = re.findall(input_pattern, content, re.IGNORECASE)
            if inputs:
                structure.append("  inputs:")
                for input_type, input_id, input_class in inputs:
                    input_info = []
                    if input_type:
                        input_info.append(f"type='{input_type}'")
                    if input_id:
                        input_info.append(f"id='{input_id}'")
                    if input_class:
                        input_info.append(f"class='{input_class}'")
                    if input_info:
                        structure.append(f"    - {' '.join(input_info)}")
            
            # ========================================
            # フォーム要素を抽出（既存機能維持）
            # ========================================
            form_pattern = r'<form[^>]*(?:id=["\']([^"\']*)["\'])?[^>]*(?:action=["\']([^"\']*)["\'])?[^>]*>'
            forms = re.findall(form_pattern, content, re.IGNORECASE)
            if forms:
                structure.append("  forms:")
                for form_id, action in forms:
                    form_info = []
                    if form_id:
                        form_info.append(f"id='{form_id}'")
                    if action:
                        form_info.append(f"action='{action}'")
                    if form_info:
                        structure.append(f"    - {' '.join(form_info)}")
            
            # ========================================
            # Phase 2追加: 全data-*属性のサマリー
            # ========================================
            if all_data_attrs:
                structure.append("  data-attributes:")
                for attr_name in sorted(all_data_attrs.keys()):
                    values = all_data_attrs[attr_name]
                    # 最大5個まで表示
                    values_display = values[:5]
                    values_str = ', '.join(f"'{v}'" for v in values_display)
                    if len(values) > 5:
                        values_str += f" (and {len(values) - 5} more)"
                    structure.append(f"    - {attr_name}: {values_str}")
            
            return structure
            
        except Exception as e:
            logger.debug(f"⚠ HTML structure extraction failed for {file_path}: {e}")
            return []

    def _extract_javascript_interfaces(self, file_path: str) -> List[str]:
            """
            JavaScriptファイルからdataset使用箇所とdata属性参照を抽出（Phase 2新規実装）
            
            Args:
                file_path: JavaScriptファイルのパス
                
            Returns:
                List[str]: JavaScript構造情報のリスト
                
            抽出内容:
                - element.dataset.xxx パターン
                - querySelector('[data-xxx]') パターン
                - getAttribute('data-xxx') パターン
                - addEventListener登録
                - 関数定義
            """
            try:
                from pathlib import Path
                import re
                
                # ファイルを読み込む
                full_path = Path.cwd() / file_path
                if not full_path.exists():
                    return []
                
                content = full_path.read_text(encoding='utf-8', errors='ignore')
                structure = []
                
                # ========================================
                # dataset使用箇所を抽出
                # ========================================
                dataset_properties = set()
                
                # element.dataset.property パターン
                dataset_pattern = r'\.dataset\.([a-zA-Z_][a-zA-Z0-9_]*)'
                matches = re.findall(dataset_pattern, content)
                dataset_properties.update(matches)
                
                if dataset_properties:
                    structure.append("  dataset-usage:")
                    for prop in sorted(dataset_properties):
                        structure.append(f"    - dataset.{prop}")
                
                # ========================================
                # data属性クエリを抽出
                # ========================================
                data_queries = set()
                
                # querySelector/querySelectorAll('[data-*]') パターン
                query_pattern1 = r'querySelector(?:All)?\(["\'][^"\']*\[data-([a-zA-Z0-9_-]+)'
                matches1 = re.findall(query_pattern1, content)
                data_queries.update(matches1)
                
                # getAttribute('data-*') パターン
                query_pattern2 = r'getAttribute\(["\']data-([a-zA-Z0-9_-]+)["\']'
                matches2 = re.findall(query_pattern2, content)
                data_queries.update(matches2)
                
                if data_queries:
                    structure.append("  data-queries:")
                    for query in sorted(data_queries):
                        structure.append(f"    - [data-{query}]")
                
                # ========================================
                # イベントリスナー登録を抽出
                # ========================================
                event_listeners = []
                
                # addEventListener パターン
                listener_pattern = r'\.addEventListener\(["\']([a-zA-Z]+)["\']\s*,\s*([a-zA-Z_][a-zA-Z0-9_]*)'
                matches = re.findall(listener_pattern, content)
                
                if matches:
                    structure.append("  event-listeners:")
                    event_types = set()
                    for event_type, handler in matches:
                        event_types.add(event_type)
                    
                    for event_type in sorted(event_types):
                        structure.append(f"    - {event_type}")
                
                # ========================================
                # 関数定義を抽出（簡易版）
                # ========================================
                functions = set()
                
                # function宣言
                func_pattern1 = r'function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\('
                matches1 = re.findall(func_pattern1, content)
                functions.update(matches1)
                
                # const/let/var 関数式
                func_pattern2 = r'(?:const|let|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(?:function|\([^)]*\)\s*=>)'
                matches2 = re.findall(func_pattern2, content)
                functions.update(matches2)
                
                if functions:
                    structure.append("  functions:")
                    for func in sorted(functions)[:20]:  # 最大20個まで
                        structure.append(f"    - {func}()")
                
                return structure
                
            except Exception as e:
                logger.debug(f"⚠ JavaScript structure extraction failed for {file_path}: {e}")
                return []

    def _extract_css_structure(self, file_path: str) -> List[str]:
            """
            CSSファイルからセレクタ情報を抽出
            
            Args:
                file_path: CSSファイルのパス
                
            Returns:
                List[str]: CSS構造情報のリスト
            """
            try:
                from pathlib import Path
                import re
                
                # ファイルを読み込む
                full_path = Path.cwd() / file_path
                if not full_path.exists():
                    return []
                
                content = full_path.read_text(encoding='utf-8', errors='ignore')
                structure = []
                
                # クラスセレクタを抽出 (.classname)
                class_pattern = r'\.([a-zA-Z_][\w-]*)\s*[,{]'
                classes = set(re.findall(class_pattern, content))
                
                # IDセレクタを抽出 (#idname)
                id_pattern = r'#([a-zA-Z_][\w-]*)\s*[,{]'
                ids = set(re.findall(id_pattern, content))
                
                if classes:
                    structure.append("  classes:")
                    for cls in sorted(classes)[:20]:  # 最大20個まで
                        structure.append(f"    - .{cls}")
                
                if ids:
                    structure.append("  ids:")
                    for id_name in sorted(ids)[:10]:  # 最大10個まで
                        structure.append(f"    - #{id_name}")
                
                return structure
                
            except Exception as e:
                logger.debug(f"⚠ CSS structure extraction failed for {file_path}: {e}")
                return []

    def _get_project_context(self) -> str:
        """
        プロジェクトコンテキストを取得(リポジトリマップを含む)
        
        LLMプロンプトに渡すためのプロジェクト全体のコンテキスト情報を生成します。
        
        ⭐ ステップ0-1で拡張:
        - リポジトリマップ全体をコンテキストに含めるように改修
        - 既存の統計情報出力はフォールバック(エラー時のみ)に変更
        
        Returns:
            str: プロジェクトコンテキスト文字列
                基本コンテキスト + リポジトリマップを含む
        """
        context_parts = []
        
        # 基本コンテキスト(Repository Analyzerのデータを優先)
        if self.repository_analyzer and hasattr(self.repository_analyzer, 'analysis_stats'):
            stats = self.repository_analyzer.analysis_stats
            if stats and stats.get('analyzed_files', 0) > 0:
                # Repository Analyzerから最新の統計情報を取得
                repo_data = self.repository_analyzer.memory.repository_data
                
                # ファイル数と言語情報
                # repository_dataは Dict[str, RepositoryFile] 型
                total_files = len(repo_data)
                
                # 言語情報を集計
                languages = {}
                for repo_file in repo_data.values():
                    lang = repo_file.language
                    if lang:
                        languages[lang] = languages.get(lang, 0) + 1
                
                # プロジェクトルート
                root_dir = self.context.root_dir if self.context else Path.cwd()
                
                # コンテキスト文字列を構築
                context_parts.append(f"# Project Context")
                context_parts.append(f"Root: {root_dir}")
                context_parts.append(f"Total files: {total_files}")
                                
                # 言語情報
                if languages:
                    languages_str = ', '.join(
                        f"{lang} ({count})" 
                        for lang, count in sorted(
                            languages.items(), 
                            key=lambda x: x[1], 
                            reverse=True
                        )
                    )
                    context_parts.append(f"Languages: {languages_str}")
        elif self.context:
            # Repository Analyzerがない場合のフォールバック
            context_str = str(self.context)
            # 長すぎる場合は省略(既存の動作を維持)
            if len(context_str) > 10000:
                context_parts.append(context_str[:10000] + "...")
            else:
                context_parts.append(context_str)
        
        # ⭐ リポジトリマップ全体を含める(新規追加)
        if self.repository_analyzer:
            try:
                # リポジトリマップを生成
                repo_map = self._generate_repository_map()
                
                if repo_map:
                    # リポジトリマップをコンテキストに追加
                    context_parts.append("\n# Repository Structure:")
                    context_parts.append(repo_map)
                else:
                    # リポジトリマップが空の場合は統計情報のみ(フォールバック)
                    stats = self.repository_analyzer.analysis_stats
                    if stats and stats.get('analyzed_files', 0) > 0:
                        analyzed_count = stats.get('analyzed_files', 0)
                        total_classes = stats.get('total_classes', 0)
                        total_functions = stats.get('total_functions', 0)
                        
                        context_parts.append(f"\n# Repository Statistics:")
                        context_parts.append(f"Analyzed files: {analyzed_count}")
                        context_parts.append(f"Total classes: {total_classes}")
                        context_parts.append(f"Total functions: {total_functions}")
            
            except Exception as e:
                # エラー時は統計情報のみ出力(既存の動作)
                try:
                    stats = self.repository_analyzer.analysis_stats
                    if stats and stats.get('analyzed_files', 0) > 0:
                        analyzed_count = stats.get('analyzed_files', 0)
                        total_classes = stats.get('total_classes', 0)
                        total_functions = stats.get('total_functions', 0)
                        
                        context_parts.append(f"\n# Repository Statistics:")
                        context_parts.append(f"Analyzed files: {analyzed_count}")
                        context_parts.append(f"Total classes: {total_classes}")
                        context_parts.append(f"Total functions: {total_functions}")
                except:
                    # 統計情報の取得も失敗した場合は何も追加しない
                    pass
        
        # ⭐ UI Knowledge を追加（新規追加）
        ui_knowledge = self._get_ui_knowledge()
        if ui_knowledge:
            context_parts.append("\n# UI Component Knowledge:")
            context_parts.append(ui_knowledge)
        
        return '\n'.join(context_parts)

    def _get_ui_knowledge(self) -> str:
        """
        UI コンポーネントの知識を取得（JSON形式）
        
        .cognix/ui-knowledge.json から UI コンポーネントの仕様を読み込み、
        LLMのコンテキストとして使用できる文字列形式で返します。
        
        優先順位:
        1. ~/.cognix/knowledge/ui-knowledge.json (ユーザー設定)
        2. ~/.cognix/ui-knowledge.json (ユーザー設定・簡易版)
        3. .cognix/ui-knowledge.json (プロジェクト内)
        
        Returns:
            str: UI knowledge JSON文字列
                 - ファイルが存在しない場合: 空文字列
                 - 読み込みエラー時: 空文字列
        """
        # 優先順位付きでパスを試す
        user_cognix_dir = Path.home() / '.cognix'
        
        # 優先順位1: ~/.cognix/knowledge/ui-knowledge.json
        ui_knowledge_file = user_cognix_dir / 'knowledge' / 'ui-knowledge.json'
        
        # 優先順位2: ~/.cognix/ui-knowledge.json
        if not ui_knowledge_file.exists():
            ui_knowledge_file = user_cognix_dir / 'ui-knowledge.json'
        
        # 優先順位3: .cognix/ui-knowledge.json (プロジェクト内)
        if not ui_knowledge_file.exists():
            ui_knowledge_file = Path(".cognix/ui-knowledge.json")
        
        # ファイルが存在しない場合は空文字列を返す
        if not ui_knowledge_file.exists():
            logger.debug("[UI Knowledge] File not found in any location")
            return ""
        
        try:
            # JSON ファイルを読み込み
            with open(ui_knowledge_file, 'r', encoding='utf-8') as f:
                knowledge_data = json.load(f)
            
            # JSON文字列に変換（整形済み、日本語も維持）
            knowledge_str = json.dumps(knowledge_data, indent=2, ensure_ascii=False)
            
            # デバッグログ
            logger.debug(f"[UI Knowledge] Loaded from {ui_knowledge_file}")
            logger.debug(f"[UI Knowledge] Size: {len(knowledge_str)} chars")
            
            return knowledge_str
        
        except json.JSONDecodeError as e:
            # JSON形式エラー
            logger.debug(f"[UI Knowledge] JSON decode error: {e}")
            return ""
        
        except Exception as e:
            # その他のエラー
            logger.debug(f"[UI Knowledge] Failed to load: {e}")
            return ""

    def _get_code_generation_system_prompt(self, goal_constraints: Dict = None) -> str:
        """
        コード生成用のシステムプロンプトを取得
        
        Args:
            goal_constraints: Goal制約情報（Phase 1実装）
        
        Returns:
            システムプロンプト文字列
        """
        # 🆕 デバッグログ: 受け取ったパラメータを確認
        logger.debug(f"[VERIFY-METHOD] _get_code_generation_system_prompt called")
        logger.debug(f"[VERIFY-METHOD] goal_constraints parameter: {goal_constraints}")
        if goal_constraints:
            logger.debug(f"[VERIFY-METHOD] constraint_strength: {goal_constraints.get('constraint_strength')}")
            logger.debug(f"[VERIFY-METHOD] keep_keywords: {goal_constraints.get('keep_keywords')}")
            logger.debug(f"[VERIFY-METHOD] only_keywords: {goal_constraints.get('only_keywords')}")
        else:
            logger.debug(f"[VERIFY-METHOD] goal_constraints is None or empty - constraint prompt will NOT be added")
        
        # 🆕 Pythonバージョン情報を取得
        import sys
        python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
        python_major_minor = f"{sys.version_info.major}.{sys.version_info.minor}"
        logger.debug(f"[VERIFY-METHOD] Python version detected: {python_version}")
        
        base_prompt = f"""You are an expert software developer assistant.

    RUNTIME ENVIRONMENT (CRITICAL - Use compatible package versions):
    - Python Version: {python_version}
    - When generating requirements.txt for Python {python_major_minor}, use ONLY packages compatible with Python {python_major_minor}
    - For Python 3.13+: Use SQLAlchemy>=2.0.25, Flask>=3.0.0, marshmallow>=3.21.0
    - For Python 3.12: Use SQLAlchemy>=2.0.20
    - For Python 3.11 and below: Standard versions are usually compatible
    - ALWAYS prefer latest stable versions of packages for better compatibility

    LIBRARY API COMPATIBILITY (CRITICAL - Use modern API, NOT deprecated):
    - marshmallow 3.x: Use `load_default=` instead of `missing=` for default values on load
    - marshmallow 3.x: Use `dump_default=` instead of `default=` for default values on dump
    - marshmallow 3.x: Example: fields.Int(load_default=0) NOT fields.Int(missing=0)
    - SQLAlchemy 2.x: Prefer `mapped_column()` over `Column()` for new ORM models
    - SQLAlchemy 2.x: Use `select()` instead of `Query.filter()` for new code
    - Flask 3.x: Use `app.json.provider_class` instead of deprecated JSON APIs
    - Pydantic 2.x: Use `model_validator` instead of `validator`, `field_validator` instead of `root_validator`

    HTML/CSS/JS FILE PLACEMENT AND REFERENCE RULES (CRITICAL):
    
    FOR FLASK/BACKEND PROJECTS (CRITICAL - Most common case):
    - ALWAYS use absolute paths starting with /static/:
      CORRECT: <link rel="stylesheet" href="/static/css/styles.css">
      CORRECT: <script src="/static/js/app.js"></script>
      WRONG:   <link rel="stylesheet" href="css/styles.css">
      WRONG:   <script src="js/app.js"></script>
    - WHY: Flask serves static files at /static/ URL path. Even if index.html is inside 
      static/ directory, the browser URL is typically '/' (via send_from_directory),
      so relative paths resolve incorrectly and cause 404 errors.
    - For Jinja2 templates, use url_for():
      BEST: <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
    
    FOR STATIC HTML (No server, opening directly in browser):
    - Use relative paths when opening HTML directly via file://
      CORRECT: <link rel="stylesheet" href="css/styles.css">
    
    GENERAL RULES:
    - ALWAYS generate ALL files referenced in HTML:
      If HTML has <link href="/static/css/styles.css">, you MUST generate static/css/styles.css
      If HTML has <script src="/static/js/app.js">, you MUST generate static/js/app.js
    - CSS and JS files MUST be in the same relative directory structure as referenced in HTML
    - NEVER create nested static/static/ directories - this indicates a path error
    
    DEFAULT: When in doubt, use /static/ prefix (Flask-style absolute paths).

    __init__.py FILES (CRITICAL - Keep them simple):
    - __init__.py files should contain ONLY import statements and __all__ exports
    - Do NOT include placeholder classes, fallback implementations, or try/except import wrappers
    - Do NOT include TODO comments or "implement later" notes
    - Trust that referenced modules exist (they are generated in the same session)
    - CORRECT example:
      ```python
      from .user_service import UserService
      from .product_service import ProductService
      
      __all__ = ['UserService', 'ProductService']
      ```
    - WRONG example (do NOT do this):
      ```python
      try:
          from .user_service import UserService
      except ImportError:
          class UserService:  # Placeholder - DO NOT DO THIS
              pass
      ```

    Your role is to generate high-quality, production-ready code based on user requirements.

    CRITICAL RULES:
    1. Always follow the exact filename specified in the user's request
    2. When asked to generate a specific file (e.g., routes/auth.js), you MUST return that exact filename
    3. Use the ## File: filename format to specify filenames
    4. Write clean, well-documented, and maintainable code
    5. Follow industry best practices and modern coding standards
    6. Include appropriate error handling and edge case management
    7. Add helpful comments for complex logic
    8. Ensure code is secure and follows security best practices

    CODE QUALITY STANDARDS:
    - Use meaningful variable and function names
    - Keep functions focused and single-purpose
    - Avoid code duplication (DRY principle)
    - Write modular and reusable code
    - Include proper type hints/annotations when applicable
    - Follow the style guide of the target language
    - Optimize for readability over cleverness

    SECURITY CONSIDERATIONS:
    - Validate all user inputs
    - Use parameterized queries for database operations
    - Implement proper authentication and authorization
    - Avoid exposing sensitive information
    - Use secure random number generators
    - Follow OWASP guidelines

    DEFENSIVE CODING REQUIREMENTS (CRITICAL - Prevents Runtime Errors):
    
    All functions that access game objects or shared state MUST include guard conditions.
    This prevents null reference errors, state-related bugs, and race conditions.

    1. NULL/UNDEFINED CHECKS (Required for any shared object access):
       Every function accessing objects like currentPiece, player, selectedItem, etc. MUST start with a guard:
       ```javascript
       function anyFunctionUsingSharedObject() {{
           if (!sharedObject) return;
           // ... rest of code
       }}
       ```
       Apply to: draw(), update(), render(), and any function that reads object properties.

    2. STATE GUARDS (Required for input handlers and game loops):
       Every input handler and update function MUST verify state before processing:
       ```javascript
       function handleInput(e) {{
           if (gameState !== 'playing') return;
           // ... handle input
       }}
       
       function gameLoop(timestamp) {{
           requestAnimationFrame(gameLoop);
           if (gameState !== 'playing') return;
           // ... update game
       }}
       ```

    3. SAFE SETTIMEOUT/CALLBACK PATTERN (Required for delayed operations):
       When using setTimeout with state-dependent functions, ALWAYS re-check state inside callback:
       ```javascript
       // WRONG - state may change during timeout
       setTimeout(spawnEnemy, 100);
       
       // CORRECT - re-check state
       setTimeout(() => {{
           if (gameState !== 'playing') return;
           spawnEnemy();
       }}, 100);
       ```

    4. BOUNDS CHECKING (Required for array/grid access):
       All array or grid access MUST validate indices:
       ```javascript
       function getCell(x, y) {{
           if (x < 0 || x >= COLS || y < 0 || y >= ROWS) return null;
           return grid[y][x];
       }}
       ```

    5. STATE TRANSITION SAFETY (Required for state changes):
       When transitioning states (start, pause, gameover), ALWAYS clean up and initialize properly:
       ```javascript
       function startGame() {{
           // Clear previous state completely
           clearAllTimers();
           resetAllVariables();
           gameState = 'playing';
           // Then initialize
           init();
       }}
       ```

    6. EVENT HANDLER SAFETY (Required for UI interactions):
       All click/touch handlers MUST check state and object existence:
       ```javascript
       button.addEventListener('click', () => {{
           if (gameState !== 'playing') return;
           if (!targetObject) return;
           performAction();
       }});
       ```

    APPLY THESE PATTERNS TO:
    - Games (currentPiece, player, enemies, bullets, particles)
    - Forms (selectedItem, currentUser, formData)
    - Canvas apps (context, currentShape, selectedTool)
    - Any application with shared mutable state

    OUTPUT FORMAT:
    When generating multiple files, use this format:
    ## File: filename1.ext
    ```
    code for file 1
    ```

    ## File: filename2.ext
    ```
    code for file 2
    ```

    When generating a single file, you may optionally use:
    ## File: filename.ext
    ```
    code
    ```

    Or simply provide the code directly if the filename is clear from context.

    USING REPOSITORY MAP:
    
    If a Repository Map is provided in the context, USE IT to understand the project structure:
    
    1. IDENTIFY ALL REQUIRED FILES:
    - The map shows existing files with their classes, methods, functions, HTML buttons, and CSS classes
    - When modifying functionality, check ALL related files in the map
    - Example: If asked to "add multiplication to calculator":
        * Check map for index.html or calculator.html (UI with buttons - you'll see existing button list)
        * Check map for calculator.js (logic with calculation methods - you'll see add(), subtract())
        * Check map for styles.css (styling for buttons - you'll see CSS classes)
        * MODIFY ALL RELEVANT FILES: If buttons are missing, add them to HTML. If methods are missing, add them to JS.

    2. GENERATE COMPLETE SOLUTIONS:
    - CRITICAL: When adding new functionality (like new operators), you MUST modify BOTH UI and logic files
    - DO NOT generate only UI changes without updating the underlying logic files
    - DO NOT generate only logic changes without updating the UI files
    - DO NOT ignore related files shown in the repository map
    - Include ALL files needed to fulfill the requirement completely
    - Example: Adding multiplication REQUIRES both HTML button changes AND JavaScript method additions

    3. RESPECT EXISTING CODE STRUCTURE:
   - Use abstractions and patterns shown in the map
   - Follow the existing project architecture
   - Maintain consistency with existing code style
   - Do NOT embed CSS in HTML if styles.css exists
   - Do NOT rewrite entire files when small changes suffice
    
    CRITICAL: If the repository map shows related files (like calculator.js when working on calculator.html),
    you MUST modify all relevant files to create a complete, functional solution.

    HTML/JAVASCRIPT INTERFACE CONSISTENCY:
    
    When working with HTML and JavaScript files together, ensure data attribute consistency:
    
    1. HTML DATA ATTRIBUTES:
       - All data-* attributes in HTML (e.g., data-action, data-number) MUST be referenced in JavaScript
       - Example: <button data-action="add"> requires JavaScript: element.dataset.action
    
    2. JAVASCRIPT DATASET USAGE:
       - All dataset properties accessed in JavaScript MUST exist in HTML
       - Example: element.dataset.operation requires HTML: data-operation="..."
    
    3. INTERFACE VALIDATION:
       - Before generating code, verify that:
         * Every HTML data-* attribute has a corresponding JavaScript reference
         * Every JavaScript dataset access has a corresponding HTML data-* attribute
       - If mismatches are detected, fix them in the generated code
    
    4. COMMON PATTERNS TO CHECK:
       - Event listeners accessing element.dataset.* properties
       - querySelectorAll('[data-*]') selectors
       - getAttribute('data-*') calls
    
    CRITICAL: Data attribute mismatches between HTML and JavaScript are a common source of runtime errors.
    Always ensure complete consistency between HTML data-* attributes and JavaScript dataset usage.

    ## Cross-File Consistency and Reference Integrity (CRITICAL)

    ### Configuration File Patterns (Python)
    When creating config.py or similar configuration files:
    - If you define a variable at module level (e.g., DATABASE_PATH = "..."), 
      you MUST also define it as a class attribute if it will be accessed via class instances
    - Example of CORRECT pattern:
      ```python
      # Module level
      DATABASE_PATH = os.path.join(BASE_DIR, 'db.sqlite')
      
      class Config:
          # Class attribute (REQUIRED for instance access)
          DATABASE_PATH = DATABASE_PATH
          DATABASE_URI = f"sqlite:///{{DATABASE_PATH}}"
      ```
    - Example of INCORRECT pattern that causes AttributeError:
      ```python
      # Module level
      DATABASE_PATH = os.path.join(BASE_DIR, 'db.sqlite')
      
      class Config:
          # DATABASE_PATH is NOT defined as class attribute ❌
          DATABASE_URI = f"sqlite:///{{DATABASE_PATH}}"  # This works at module level
      
      # In another file:
      config = get_config()  # Returns Config instance
      config.DATABASE_PATH  # ❌ AttributeError: type object 'Config' has no attribute 'DATABASE_PATH'
      ```

    ### Import and Attribute Access Verification
    Before using any attribute from an imported module:
    - VERIFY that the attribute actually exists in the target module
    - If importing from config.py, check what attributes are defined as class attributes vs module variables
    - Use consistent access patterns throughout the codebase

    ### Cross-File Reference Checklist
    Before generating any file that imports from another file:
    1. Review the imported module's structure (class vs module level definitions)
    2. Ensure all accessed attributes are actually defined in the target
    3. Use the same access pattern consistently (e.g., if using config.DATABASE_PATH, ensure DATABASE_PATH is a class attribute)
    4. For configuration values, prefer class attributes over module variables for better testability and consistency

    ## EMOJI USAGE POLICY (CRITICAL)
    
    NEVER use emojis (📝, ✏️, ❤️, 🎉, ✨, etc.) in UI text, headers, or content unless explicitly requested by the user.
    
    WHY: Emojis make applications look unprofessional and cheap. Professional applications use clean text and proper iconography.
    
    RULES:
    1. DO NOT use emojis in:
       - Headings (<h1>, <h2>, etc.)
       - Button text
       - Labels and placeholders
       - Empty states and messages
       - Footer text
       - Any user-facing text
    
    2. EXCEPTION: Only use emojis if the user specifically requests them in their goal
       - Examples of explicit requests: "add emoji", "use 🎉 icon", "include ❤️ symbol"
       - If the request is ambiguous, default to NO emojis
    
    3. USE INSTEAD:
       - SVG icons (preferred for web apps)
       - Icon fonts (Font Awesome, Material Icons, Heroicons)
       - Clean text with proper typography
       - CSS styling and colors for visual interest
       - Professional unicode symbols (•, →, ✓, ×) where minimal decoration is needed
    
    CORRECT EXAMPLES:
    ✅ <h1>My Todo List</h1>
    ✅ <h1 class="app-title">Todo List</h1>
    ✅ <button class="btn-add">Add Task</button>
    ✅ <button class="btn-add"><svg>...</svg> Add</button>
    ✅ <p class="empty-text">No tasks yet. Add one to get started!</p>
    ✅ <p>Built with HTML, CSS & JavaScript</p>
    
    INCORRECT EXAMPLES (NEVER DO THIS):
    ❌ <h1>📝 My Todo List</h1>
    ❌ <h1>My Todo List ✏️</h1>
    ❌ <button class="btn-add">Add ✏️</button>
    ❌ <button class="btn-add">Add +</button> ← Use <svg> or icon font instead
    ❌ <p class="empty-text">✨ No tasks yet. Add one to get started!</p>
    ❌ <p>Built with ❤️ using vanilla JavaScript</p>
    
    REMEMBER: 
    - Professional applications are emoji-free by default
    - Clean typography > Emojis
    - Icons > Emojis
    - If unsure, DO NOT use emojis

    ## MANDATORY FEATURE IMPLEMENTATION (CRITICAL - DO NOT SKIP)

    When the user's goal mentions specific features, they are MANDATORY requirements, not optional enhancements.
    You MUST implement ALL features explicitly mentioned in the goal, even if the code becomes longer.

    ### Priority Rules:
    1. NEVER skip features due to code length or token limits
    2. If you cannot implement everything, prioritize in this order:
       - Core functionality (game logic, main features) - HIGHEST
       - Audio/Sound (Web Audio API, effects) - HIGH
       - Visual effects (particles, animations) - HIGH
       - UI feedback (popups, warnings, transitions) - HIGH
       - Polish/decoration (gradients, glows) - MEDIUM
    3. NEVER silently omit features - if truly impossible, add a TODO comment explaining why

    ### Mandatory Feature Keywords:
    When you see these keywords in the goal, they MUST be implemented:

    **Audio/Sound (MUST implement if mentioned):**
    - "Web Audio API" → Implement AudioContext with oscillators/buffers
    - "sound effects" / "sfx" → Implement playable sound functions
    - "audio" / "music" / "bgm" → Implement audio playback system
    - "mute" / "volume" → Implement audio control

    **Visual Effects (MUST implement if mentioned):**
    - "particle" / "particles" → Implement particle system with spawn/update/render
    - "screen shake" / "shake" → Implement camera/canvas shake effect
    - "slow motion" / "slowmo" → Implement time scale modification
    - "flash" / "screen flash" → Implement overlay flash effect
    - "explosion" → Implement explosion animation with particles
    - "trail" / "motion trail" → Implement position history rendering

    **UI Feedback (MUST implement if mentioned):**
    - "popup" / "float text" / "floating" → Implement animated text that rises and fades
    - "WARNING" / "alert" → Implement flashing warning text/screen
    - "combo" → Implement combo counter with visual feedback
    - "transition" → Implement screen/state transition animations

    **Visual Style (MUST implement if mentioned):**
    - "glassmorphism" / "frosted" / "blur" → Implement backdrop-filter blur effect
    - "neon" / "glow" → Implement CSS/canvas glow effects (box-shadow, filter)
    - "CRT" / "scanline" / "retro" → Implement scanline overlay effect
    - "gradient" → Implement gradient backgrounds/fills
    - "parallax" → Implement multi-layer scrolling effect
    - "vignette" → Implement edge darkening effect

    **Background Effects (MUST implement if mentioned):**
    - "animated background" → Implement moving/animated background elements
    - "grid" / "grid lines" → Implement grid pattern (static or animated)
    - "star field" / "stars" → Implement star/particle background

    **Controls (MUST implement if mentioned):**
    - Key bindings (e.g., "P for pause", "M for mute") → Implement keyboard handlers
    - Mouse controls → Implement mouse event handlers
    - Touch controls → Implement touch event handlers

    ### Implementation Patterns:

    **Web Audio API Basic Pattern:**
    ```javascript
    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    function playSound(frequency, duration, type = 'sine') {{
      const oscillator = audioCtx.createOscillator();
      const gainNode = audioCtx.createGain();
      oscillator.connect(gainNode);
      gainNode.connect(audioCtx.destination);
      oscillator.frequency.value = frequency;
      oscillator.type = type;
      gainNode.gain.setValueAtTime(0.3, audioCtx.currentTime);
      gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
      oscillator.start(audioCtx.currentTime);
      oscillator.stop(audioCtx.currentTime + duration);
    }}
    ```

    **Particle System Basic Pattern:**
    ```javascript
    const particles = [];
    function createParticle(x, y, color) {{
      particles.push({{ x, y, color, vx: (Math.random()-0.5)*10, vy: (Math.random()-0.5)*10, life: 1.0, decay: 0.02 }});
    }}
    function updateParticles(dt) {{
      for (let i = particles.length - 1; i >= 0; i--) {{
        const p = particles[i];
        p.x += p.vx * dt; p.y += p.vy * dt; p.life -= p.decay;
        if (p.life <= 0) particles.splice(i, 1);
      }}
    }}
    ```

    **Screen Shake Basic Pattern:**
    ```javascript
    let shakeAmount = 0;
    function triggerShake(intensity) {{ shakeAmount = intensity; }}
    function applyShake(ctx) {{
      if (shakeAmount > 0.5) {{
        ctx.translate((Math.random()-0.5)*shakeAmount, (Math.random()-0.5)*shakeAmount);
        shakeAmount *= 0.9;
      }}
    }}
    ```

    **Floating Text Basic Pattern:**
    ```javascript
    const floatingTexts = [];
    function createFloatingText(x, y, text, color) {{
      floatingTexts.push({{ x, y, text, color, vy: -2, alpha: 1.0, decay: 0.02 }});
    }}
    function renderFloatingTexts(ctx) {{
      for (let i = floatingTexts.length - 1; i >= 0; i--) {{
        const ft = floatingTexts[i];
        ctx.globalAlpha = ft.alpha;
        ctx.fillStyle = ft.color;
        ctx.fillText(ft.text, ft.x, ft.y);
        ft.y += ft.vy; ft.alpha -= ft.decay;
        if (ft.alpha <= 0) floatingTexts.splice(i, 1);
      }}
      ctx.globalAlpha = 1.0;
    }}
    ```

    ### Technology Choice Rules:
    - "Pure HTML + CSS + JS" / "vanilla" / "no framework" → DO NOT use React/Vue/Angular
    - "React" / "React component" → Use React
    - "Vue" / "Vue component" → Use Vue
    - No specification → Choose appropriate technology based on complexity
    
    CRITICAL: If the goal says "Pure HTML + CSS + JS" or "no frameworks", you MUST use vanilla JavaScript only.

    Remember: Always prioritize code quality, security, and maintainability."""
        
        # 🆕 フレームワーク禁止モードの場合、強制プロンプトを追加
        if getattr(self, '_no_framework_mode', False):
            no_framework_prompt = """

    ## ⚠️ FRAMEWORK PROHIBITION (ABSOLUTE - VIOLATING THIS = COMPLETE REJECTION)

    YOU ARE STRICTLY PROHIBITED FROM USING ANY FRAMEWORKS.

    **FORBIDDEN (DO NOT USE UNDER ANY CIRCUMSTANCES):**
    - React, Vue, Angular, Svelte, or ANY JavaScript framework
    - JSX syntax (no <Component />, no className, no onClick={handler})
    - import React from 'react' or any framework imports
    - useState, useEffect, useRef, or any React hooks
    - package.json with framework dependencies (react, vue, angular)
    - Component-based architecture

    **REQUIRED (YOU MUST USE):**
    - Pure HTML5 (<html>, <head>, <body>, <div>, <canvas>, etc.)
    - Pure CSS3 (no CSS-in-JS, no styled-components)
    - Pure vanilla JavaScript:
      * document.getElementById(), document.querySelector()
      * element.addEventListener()
      * Direct DOM manipulation
    - Single HTML file with embedded <style> and <script>, OR separate .html, .css, .js files

    **CORRECT FILE STRUCTURE:**
    - index.html (main HTML with structure)
    - styles.css (all CSS styles)
    - script.js (ALL application logic in vanilla JS - THIS MUST CONTAIN REAL CODE)

    **EXAMPLES OF WHAT YOU MUST GENERATE:**
    ```javascript
    // script.js - CORRECT (vanilla JS)
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    document.addEventListener('keydown', handleKeyDown);
    function gameLoop() { requestAnimationFrame(gameLoop); }
    ```

    **EXAMPLES OF WHAT IS FORBIDDEN:**
    ```jsx
    // FORBIDDEN - React/JSX
    import React from 'react';
    const App = () => { return <canvas ref={canvasRef} />; };
    ```

    ⚠️ IF YOU GENERATE REACT/VUE/ANGULAR CODE, THE ENTIRE OUTPUT WILL BE REJECTED.
    ⚠️ script.js MUST CONTAIN ACTUAL GAME/APP LOGIC, NOT COMMENTS ABOUT OTHER FILES.
"""
            base_prompt += no_framework_prompt
            logger.debug("[System Prompt] Added no-framework prohibition prompt")
        
        # 🆕 Phase 1実装: Goal制約の動的追加
        logger.debug(f"[VERIFY-METHOD] Checking constraint condition...")
        logger.debug(f"[VERIFY-METHOD] goal_constraints is truthy: {bool(goal_constraints)}")
        if goal_constraints:
            logger.debug(f"[VERIFY-METHOD] constraint_strength value: {goal_constraints.get('constraint_strength')}")
            logger.debug(f"[VERIFY-METHOD] constraint_strength == 'strict': {goal_constraints.get('constraint_strength') == 'strict'}")
        
        # 🆕 Phase 1ルール（基本ルール）を常に適用（単一ファイル生成時も有効化）
        try:
            rule_parser = FileReferenceRuleParser(logger=logger)
            rule_parser.load_rules(project_root=self.workspace_path)
            # Phase 1（Critical/High優先度）のルールのみ取得
            phase1_rules = rule_parser.get_applicable_rules(
                filepaths=['*.html', '*.css', '*.js', '*.py'],  # 汎用的なファイルタイプ
                max_phase=1  # Phase 1のみ
            )
            if phase1_rules:
                logger.debug(f"[System Prompt] Applying {len(phase1_rules)} Phase 1 rules to system prompt")
                rules_prompt = "\n\n    ## File Reference Rules (Always Applied)\n"
                for rule in phase1_rules:
                    rules_prompt += f"\n    {rule.prompt}\n"
                base_prompt += rules_prompt
        except Exception as e:
            logger.debug(f"[System Prompt] Failed to load Phase 1 rules: {e}")
        
        if goal_constraints and goal_constraints.get("constraint_strength") == "strict":
            logger.debug(f"[VERIFY-METHOD] ✅ Constraint condition TRUE - generating constraint prompt")
            constraint_prompt = self._build_constraint_enforcement_prompt(goal_constraints)
            logger.debug(f"[VERIFY-METHOD] Constraint prompt generated, length: {len(constraint_prompt)} chars")
            logger.debug(f"[VERIFY-METHOD] Adding constraint prompt to base_prompt")
            final_prompt = base_prompt + "\n\n" + constraint_prompt
            logger.debug(f"[VERIFY-METHOD] Final system prompt length: {len(final_prompt)} chars")
            return final_prompt
        
        # 制約なしの場合はベースプロンプトを返す
        logger.debug(f"[VERIFY-METHOD] ❌ Constraint condition FALSE - returning base_prompt only")
        logger.debug(f"[VERIFY-METHOD] Base prompt length: {len(base_prompt)} chars")
        return base_prompt
    def _build_constraint_enforcement_prompt(self, constraints: Dict) -> str:
        """
        制約強制プロンプトを生成（🆕 Phase 1実装）
        
        Args:
            constraints: Goal制約情報
        
        Returns:
            制約強制プロンプト文字列
        """
        enforcement = ["CRITICAL CONSTRAINTS (MUST FOLLOW):"]
        
        if constraints.get("keep_keywords"):
            enforcement.append(f"\n1. KEEP EXISTING: {', '.join(constraints['keep_keywords'])}")
            enforcement.append("   - DO NOT modify these elements")
            enforcement.append("   - DO NOT change response formats")
            enforcement.append("   - DO NOT alter existing routes/endpoints")
            enforcement.append("   - DO NOT change existing behavior")
        
        if constraints.get("only_keywords"):
            enforcement.append(f"\n2. ONLY FILES: {', '.join(constraints['only_keywords'])}")
            enforcement.append("   - ONLY create explicitly specified files")
            enforcement.append("   - DO NOT add convenience files (HTML, CSS, etc.)")
            enforcement.append("   - DO NOT add any other files not explicitly mentioned")
        
        if constraints.get("forbidden_actions"):
            enforcement.append(f"\n3. FORBIDDEN ACTIONS:")
            for action in constraints["forbidden_actions"]:
                enforcement.append(f"   - {action}")
        
        enforcement.append("\n⚠️ CRITICAL: Violating these constraints will result in COMPLETE REJECTION of the code.")
        enforcement.append("⚠️ CRITICAL: Follow these constraints EXACTLY as specified.")
        
        return "\n".join(enforcement)


    def _determine_language(self, goal: str) -> str:
        """Determine programming language from goal"""
        goal_lower = goal.lower()
        
        if "html" in goal_lower or "web page" in goal_lower:
            return "html"
        elif "javascript" in goal_lower or ".js" in goal_lower:
            return "javascript"
        elif "css" in goal_lower:
            return "css"
        elif "python" in goal_lower or ".py" in goal_lower:
            return "python"
        elif "java " in goal_lower and "javascript" not in goal_lower:
            return "java"
        elif "c++" in goal_lower or "cpp" in goal_lower:
            return "cpp"
        elif "rust" in goal_lower:
            return "rust"
        else:
            return "python"  # Default

    def _detect_multi_file_requirement(self, goal: str) -> dict:
        """
        ユーザー入力から複数ファイル生成が必要かを検出
        
        新実装: Detector Pipeline を使用
        
        Args:
            goal: ユーザーの実装目標
        
        Returns:
            {
                'required': bool,
                'reason': str,
                'file_types': list,
                'confidence': str,
                'detectors': list
            }
        """
        try:
            # Detector Pipeline を使用
            detector = FileRequirementDetector(
                logger=logger,
                repository_analyzer=self.repository_analyzer
            )
            
            # コンテキスト構築
            context = {}
            
            # 既存ファイルがあれば追加
            if hasattr(self, '_complexity_assessor') and \
               hasattr(self._complexity_assessor, 'existing_files'):
                context['existing_files'] = self._complexity_assessor.existing_files
            
            # 検出実行
            aggregated_result = detector.detect(goal, context)
            
            # 既存形式に変換
            return aggregated_result.to_legacy_dict()
        
        except Exception as e:
            # フォールバック: エラー時は安全な値を返す
            logger.error(f"[Multi-File Detection] Pipeline error: {e}")
            import traceback
            logger.error(f"[Multi-File Detection] Traceback: {traceback.format_exc()}")
            return {
                'required': False,
                'reason': f'Detection pipeline error: {e}',
                'file_types': [],
                'confidence': 'low',
                'detectors': []
            }

    def _build_multi_file_prompt(
        self, 
        expected_files: Optional[List[FileRequirement]] = None,
        max_phase: int = 3
    ) -> str:
        """
        複数ファイル生成のためのプロンプトを構築（ルールファイルベース）
        
        ルールファイル（.cognix/file_reference_rules.md）から適用可能なルールを読み込み、
        ファイル参照整合性に関するプロンプトを動的に生成する。
        
        Args:
            expected_files: 生成予定のファイルリスト（FileRequirementのリスト）
            max_phase: 適用する最大フェーズ（1: 基本ルールのみ, 2: 拡張ルールまで, 3: すべて）
        
        Returns:
            構築されたプロンプト文字列
        """
        if not expected_files:
            # ファイルリストがない場合は基本的なプロンプトのみ
            return """
    IMPORTANT: This project requires modifications to multiple files.
    Use the repository map to identify ALL files that need changes.
    Return multiple files using this format:
    ## File: filename1
```
    code for file 1
```

    ## File: filename2
```
    code for file 2
```
    """
        
        # ファイルパスのリストを抽出
        filepaths = [f.filepath for f in expected_files]
        
        # 基本的なファイルリスト
        file_list = '\n'.join([f'- {f.filepath}' for f in expected_files])
        
        prompt = f"""
    CRITICAL: You MUST generate ALL of the following files. Do NOT skip any file. Do NOT merge files into one.
    
    Required files (generate ALL):
    {file_list}
    
    IMPORTANT RULES:
    1. Generate EACH file separately using the ## File: format
    2. Do NOT create any files other than those listed above
    3. Do NOT merge multiple files into one (e.g., do NOT create a single "styles.css" instead of multiple CSS files)
    4. Each file must be complete and functional
    
    """
        
        # ルールファイルを読み込んで適用可能なルールを取得
        try:
            rule_parser = FileReferenceRuleParser(logger=logger)
            rule_parser.load_rules(project_root=self.workspace_path)
            applicable_rules = rule_parser.get_applicable_rules(filepaths, max_phase=max_phase)
            
            if applicable_rules:
                logger.debug(f"[Prompt Construction] 📋 Applying {len(applicable_rules)} file reference rules")
                
                # 適用可能なルールのプロンプトを追加
                for rule in applicable_rules:
                    logger.debug(f"[Prompt Construction] Applying rule: {rule.name} (Phase {rule.phase}, {rule.priority})")
                    prompt += rule.prompt + "\n\n"
            else:
                logger.debug("[Prompt Construction] No applicable file reference rules found")
        
        except Exception as e:
            logger.warning(f"[Prompt Construction] Failed to load file reference rules: {e}")
            # ルールファイルの読み込みに失敗しても、基本的なプロンプトは提供
        
        # 共通の指示
        prompt += f"""
    OUTPUT FORMAT (strictly follow):
    For EACH of the {len(expected_files)} files listed above, use this exact format:
    
    ## File: filename1
```
    code for file 1
```

    ## File: filename2
```
    code for file 2
```

    Remember: Generate ALL {len(expected_files)} files. Missing even one file is a critical error.
    """
        
        return prompt



    def _generate_fallback_code(self, goal: str) -> Dict[str, str]:
        """
        Generate basic fallback code when LLM is unavailable
        
        修正点:
        - 全言語対応(HTML/JavaScript/CSS/Python等)
        - 各言語の基本テンプレート実装
        """
        goal_lower = goal.lower()
        
        # ============================================
        # HTML形式の判定
        # ============================================
        if "html" in goal_lower or "web page" in goal_lower or "website" in goal_lower:
            if "clock" in goal_lower:
                filename = "digital_clock.html"
                code = '''<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Digital Clock</title>
        <style>
            body {
                margin: 0;
                padding: 0;
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            }
            
            .clock-container {
                background: rgba(255, 255, 255, 0.1);
                backdrop-filter: blur(10px);
                border-radius: 20px;
                padding: 60px 80px;
                box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
                border: 1px solid rgba(255, 255, 255, 0.18);
            }
            
            .clock {
                font-size: 80px;
                font-weight: 700;
                color: #fff;
                text-shadow: 0 0 20px rgba(255, 255, 255, 0.5),
                            0 0 40px rgba(255, 255, 255, 0.3);
                letter-spacing: 8px;
            }
            
            .date {
                font-size: 24px;
                color: rgba(255, 255, 255, 0.8);
                margin-top: 20px;
                text-align: center;
            }
        </style>
    </head>
    <body>
        <div class="clock-container">
            <div class="clock" id="clock">00:00:00</div>
            <div class="date" id="date"></div>
        </div>
        
        <script>
            function updateClock() {
                const now = new Date();
                
                // Time
                const hours = String(now.getHours()).padStart(2, '0');
                const minutes = String(now.getMinutes()).padStart(2, '0');
                const seconds = String(now.getSeconds()).padStart(2, '0');
                document.getElementById('clock').textContent = `${hours}:${minutes}:${seconds}`;
                
                // Date
                const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
                const dateStr = now.toLocaleDateString('en-US', options);
                document.getElementById('date').textContent = dateStr;
            }
            
            updateClock();
            setInterval(updateClock, 1000);
        </script>
    </body>
    </html>'''
            
            elif "calculator" in goal_lower:
                filename = "calculator.html"
                code = '''<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Calculator</title>
        <style>
            body {
                margin: 0;
                padding: 0;
                display: flex;
                justify-content: center;
                align-items: center;
                min-height: 100vh;
                background: #f0f0f0;
                font-family: Arial, sans-serif;
            }
            
            .calculator {
                background: #fff;
                border-radius: 10px;
                box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
                padding: 20px;
                width: 300px;
            }
            
            .display {
                background: #222;
                color: #fff;
                font-size: 32px;
                padding: 20px;
                text-align: right;
                border-radius: 5px;
                margin-bottom: 20px;
                min-height: 50px;
            }
            
            .buttons {
                display: grid;
                grid-template-columns: repeat(4, 1fr);
                gap: 10px;
            }
            
            button {
                padding: 20px;
                font-size: 18px;
                border: none;
                border-radius: 5px;
                cursor: pointer;
                background: #f5f5f5;
                transition: background 0.2s;
            }
            
            button:hover {
                background: #e0e0e0;
            }
            
            button.operator {
                background: #ff9500;
                color: white;
            }
            
            button.operator:hover {
                background: #e08500;
            }
        </style>
    </head>
    <body>
        <div class="calculator">
            <div class="display" id="display">0</div>
            <div class="buttons">
                <button onclick="clearDisplay()">C</button>
                <button onclick="appendToDisplay('/')" class="operator">/</button>
                <button onclick="appendToDisplay('*')" class="operator">*</button>
                <button onclick="deleteLast()">←</button>
                
                <button onclick="appendToDisplay('7')">7</button>
                <button onclick="appendToDisplay('8')">8</button>
                <button onclick="appendToDisplay('9')">9</button>
                <button onclick="appendToDisplay('-')" class="operator">-</button>
                
                <button onclick="appendToDisplay('4')">4</button>
                <button onclick="appendToDisplay('5')">5</button>
                <button onclick="appendToDisplay('6')">6</button>
                <button onclick="appendToDisplay('+')" class="operator">+</button>
                
                <button onclick="appendToDisplay('1')">1</button>
                <button onclick="appendToDisplay('2')">2</button>
                <button onclick="appendToDisplay('3')">3</button>
                <button onclick="calculate()" class="operator" style="grid-row: span 2">=</button>
                
                <button onclick="appendToDisplay('0')" style="grid-column: span 2">0</button>
                <button onclick="appendToDisplay('.')">.</button>
            </div>
        </div>
        
        <script>
            let currentDisplay = '0';
            
            function updateDisplay() {
                document.getElementById('display').textContent = currentDisplay;
            }
            
            function appendToDisplay(value) {
                if (currentDisplay === '0') {
                    currentDisplay = value;
                } else {
                    currentDisplay += value;
                }
                updateDisplay();
            }
            
            function clearDisplay() {
                currentDisplay = '0';
                updateDisplay();
            }
            
            function deleteLast() {
                currentDisplay = currentDisplay.slice(0, -1) || '0';
                updateDisplay();
            }
            
            function calculate() {
                try {
                    currentDisplay = String(eval(currentDisplay));
                } catch (e) {
                    currentDisplay = 'Error';
                }
                updateDisplay();
            }
        </script>
    </body>
    </html>'''
            
            else:
                # 汎用HTML
                filename = "index.html"
                code = f'''<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{goal.title()}</title>
        <style>
            body {{
                font-family: Arial, sans-serif;
                margin: 40px;
                background: #f5f5f5;
            }}
            .container {{
                max-width: 800px;
                margin: 0 auto;
                background: white;
                padding: 40px;
                border-radius: 8px;
                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            }}
            h1 {{
                color: #333;
            }}
        </style>
    </head>
    <body>
        <div class="container">
            <h1>{goal}</h1>
            <p>Implementation generated by Cognix Semi-Auto Engine</p>
        </div>
    </body>
    </html>'''
            
            return {filename: code}
        
        # ============================================
        # JavaScript形式の判定
        # ============================================
        elif "javascript" in goal_lower or "js " in goal_lower or ".js" in goal_lower:
            filename = "script.js"
            
            if "game" in goal_lower:
                code = '''/**
    * Simple Game Implementation
    * Generated by Cognix Semi-Auto Engine
    */

    class Game {
        constructor() {
            this.score = 0;
            this.isRunning = false;
        }
        
        start() {
            console.log('Game starting...');
            this.isRunning = true;
            this.score = 0;
            this.gameLoop();
        }
        
        gameLoop() {
            if (!this.isRunning) return;
            
            // Game logic here
            this.update();
            this.render();
            
            requestAnimationFrame(() => this.gameLoop());
        }
        
        update() {
            // Update game state
            this.score++;
        }
        
        render() {
            // Render game
            console.log(`Score: ${this.score}`);
        }
        
        stop() {
            console.log('Game stopped');
            this.isRunning = false;
        }
    }

    // Initialize and start game
    const game = new Game();
    game.start();

    // Stop after 5 seconds (example)
    setTimeout(() => game.stop(), 5000);
    '''
            else:
                # 汎用JavaScript
                code = f'''/**
    * {goal}
    * Generated by Cognix Semi-Auto Engine
    */

    class Application {{
        constructor() {{
            this.data = [];
            this.init();
        }}
        
        init() {{
            console.log('Initializing application...');
            this.setupEventListeners();
        }}
        
        setupEventListeners() {{
            // Add event listeners here
            document.addEventListener('DOMContentLoaded', () => {{
                this.run();
            }});
        }}
        
        run() {{
            console.log('Application running');
            // Main application logic here
        }}
        
        handleError(error) {{
            console.error('Error:', error);
        }}
    }}

    // Initialize application
    try {{
        const app = new Application();
    }} catch (error) {{
        console.error('Failed to initialize:', error);
    }}
    '''
            
            return {filename: code}
        
        # ============================================
        # CSS形式の判定
        # ============================================
        elif "css" in goal_lower or "stylesheet" in goal_lower:
            filename = "styles.css"
            code = f'''/**
    * {goal}
    * Generated by Cognix Semi-Auto Engine
    */

    /* ==========================================
    Reset and Base Styles
    ========================================== */

    * {{
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }}

    html {{
        font-size: 16px;
        scroll-behavior: smooth;
    }}

    body {{
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        line-height: 1.6;
        color: #333;
        background: #f5f5f5;
    }}

    /* ==========================================
    Layout
    ========================================== */

    .container {{
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
    }}

    /* ==========================================
    Typography
    ========================================== */

    h1, h2, h3, h4, h5, h6 {{
        margin-bottom: 1rem;
        font-weight: 600;
    }}

    h1 {{ font-size: 2.5rem; }}
    h2 {{ font-size: 2rem; }}
    h3 {{ font-size: 1.75rem; }}

    p {{
        margin-bottom: 1rem;
    }}

    /* ==========================================
    Utilities
    ========================================== */

    .text-center {{ text-align: center; }}
    .text-right {{ text-align: right; }}
    .mt-1 {{ margin-top: 0.5rem; }}
    .mt-2 {{ margin-top: 1rem; }}
    .mb-1 {{ margin-bottom: 0.5rem; }}
    .mb-2 {{ margin-bottom: 1rem; }}

    /* ==========================================
    Responsive Design
    ========================================== */

    @media (max-width: 768px) {{
        .container {{
            padding: 10px;
        }}
        
        h1 {{ font-size: 2rem; }}
        h2 {{ font-size: 1.5rem; }}
    }}
    '''
            
            return {filename: code}
        
        # ============================================
        # Python形式の判定
        # ============================================
        elif "calculator" in goal_lower and "python" in goal_lower:
            filename = "calculator.py"
            code = '''#!/usr/bin/env python3
    """
    Simple Calculator
    Generated by Cognix Semi-Auto Engine
    """

    class Calculator:
        """Basic calculator with arithmetic operations"""
        
        def add(self, a: float, b: float) -> float:
            """Add two numbers"""
            return a + b
        
        def subtract(self, a: float, b: float) -> float:
            """Subtract b from a"""
            return a - b
        
        def multiply(self, a: float, b: float) -> float:
            """Multiply two numbers"""
            return a * b
        
        def divide(self, a: float, b: float) -> float:
            """Divide a by b"""
            if b == 0:
                raise ValueError("Cannot divide by zero")
            return a / b


    def main():
        """Main function"""
        calc = Calculator()
        
        err_console.print("Simple Calculator")
        err_console.print("-" * 40)
        
        try:
            a = float(input("Enter first number: "))
            op = input("Enter operation (+, -, *, /): ")
            b = float(input("Enter second number: "))
            
            if op == '+':
                result = calc.add(a, b)
            elif op == '-':
                result = calc.subtract(a, b)
            elif op == '*':
                result = calc.multiply(a, b)
            elif op == '/':
                result = calc.divide(a, b)
            else:
                err_console.print(f"Unknown operation: {op}")
                return
            
            err_console.print(f"Result: {a} {op} {b} = {result}")
            
        except ValueError as e:
            err_console.print(f"Input error: {e}")
        except Exception as e:
            err_console.print(f"Calculation error: {e}")


    if __name__ == "__main__":
        main()
    '''
            
            return {filename: code}
        
        # ============================================
        # Java形式の判定
        # ============================================
        elif "java " in goal_lower and "javascript" not in goal_lower:
            filename = "Main.java"
            code = f'''/**
    * {goal}
    * Generated by Cognix Semi-Auto Engine
    */

    public class Main {{
        public static void main(String[] args) {{
            System.out.println("Application starting...");
            
            try {{
                // Main application logic here
                run();
            }} catch (Exception e) {{
                System.err.println("Error: " + e.getMessage());
                e.printStackTrace();
            }}
        }}
        
        private static void run() {{
            // Implementation for: {goal}
            System.out.println("Running application");
        }}
    }}
    '''
            
            return {filename: code}
        
        # ============================================
        # C++形式の判定
        # ============================================
        elif "c++" in goal_lower or "cpp" in goal_lower:
            filename = "main.cpp"
            code = f'''/**
    * {goal}
    * Generated by Cognix Semi-Auto Engine
    */

    #include <iostream>
    #include <string>

    using namespace std;

    class Application {{
    public:
        Application() {{
            cout << "Application initialized" << endl;
        }}
        
        void run() {{
            cout << "Running application..." << endl;
            // Implementation for: {goal}
        }}
        
        ~Application() {{
            cout << "Application terminated" << endl;
        }}
    }};

    int main() {{
        try {{
            Application app;
            app.run();
            return 0;
        }} catch (const exception& e) {{
            cerr << "Error: " << e.what() << endl;
            return 1;
        }}
    }}
    '''
            
            return {filename: code}
        
        # ============================================
        # Rust形式の判定
        # ============================================
        elif "rust" in goal_lower:
            filename = "main.rs"
            code = f'''/**
    * {goal}
    * Generated by Cognix Semi-Auto Engine
    */

    fn main() {{
        println!("Application starting...");
        
        match run() {{
            Ok(_) => println!("Application completed successfully"),
            Err(e) => eprintln!("Error: {{}}", e),
        }}
    }}

    fn run() -> Result<(), Box<dyn std::error::Error>> {{
        // Implementation for: {goal}
        println!("Running application");
        Ok(())
    }}
    '''
            
            return {filename: code}
        
        # ============================================
        # デフォルト(Python)
        # ============================================
        else:
            filename = "main.py"
            code = f'''#!/usr/bin/env python3
    """
    {goal.title()} Implementation
    Generated by Cognix Semi-Auto Engine
    """


    def main():
        """Main function for {goal}"""
        err_console.print("Implementation for: {goal}")
        
        try:
            # Add your implementation here
            pass
            
        except Exception as e:
            err_console.print(f"Error: {{e}}")


    if __name__ == "__main__":
        main()
    '''
            
            return {filename: code}

    def _calculate_lint_error_score(
        self,
        file_errors: List[Dict],
        file_warnings: List[Dict]
    ) -> float:
        """
        Lintエラースコアを計算（エラー種類別の重み付け）
        
        エラー分類:
        - Critical (E999): 構文エラー
        - High (E9系): インデント、文法エラー
        - Medium (F4系, F8系): 未使用変数、import順序
        - Low (E5系等): スタイル違反（行が長い等）
        - Environment (F821のimport関連): 環境依存エラー（減点なし）
        
        Args:
            file_errors: このファイルのエラーリスト
            file_warnings: このファイルの警告リスト
        
        Returns:
            0.0-1.0のスコア（1.0が完璧）
        """
        # エラー分類定義
        CRITICAL_CODES = ['E999']  # 構文エラー
        HIGH_CODES = ['E9']  # E9で始まる（インデント等）
        MEDIUM_CODES = ['F4', 'F8', 'F401', 'F841']  # 未使用import/変数
        ENV_CODES = ['F821', 'F823']  # undefined name（環境依存の可能性）
        # それ以外（E501, E701等）はLOW
        
        critical_count = 0
        high_count = 0
        medium_count = 0
        low_count = 0
        env_count = 0
        
        for error in file_errors:
            code = error.get('code', '')
            message = error.get('message', '').lower()
            
            # 環境依存エラーの判定
            if any(code.startswith(ec) for ec in ENV_CODES):
                # F821/F823でimport関連のエラーは環境問題
                if 'import' in message or 'module' in message:
                    env_count += 1
                    continue
            
            # エラー分類
            if any(code.startswith(cc) for cc in CRITICAL_CODES):
                critical_count += 1
            elif any(code.startswith(hc) for hc in HIGH_CODES):
                high_count += 1
            elif any(code.startswith(mc) for mc in MEDIUM_CODES):
                medium_count += 1
            else:
                low_count += 1
        
        # 警告は軽微
        warning_count = len(file_warnings)
        
        # スコア計算（重み付け）
        score = 1.0
        score -= critical_count * 0.15   # Critical: -15% / 個（構文エラー）
        score -= high_count * 0.10       # High: -10% / 個（インデント等）
        score -= medium_count * 0.05     # Medium: -5% / 個（未使用変数）
        score -= low_count * 0.02        # Low: -2% / 個（スタイル違反）
        score -= warning_count * 0.01    # Warning: -1% / 個
        # 環境依存エラー（env_count）は減点しない
        
        # デバッグログ出力
        if critical_count + high_count + medium_count + low_count + env_count > 0:
            logger.debug(
                f"[Quality] Lint errors breakdown: Critical={critical_count}, High={high_count}, "
                f"Medium={medium_count}, Low={low_count}, Env={env_count}, Warnings={warning_count}"
            )
        
        return max(0.0, score)

    def _get_error_breakdown(
        self,
        errors: List[Dict],
        warnings: List[Dict]
    ) -> Dict[str, int]:
        """
        Lintエラーを種類別に分類してカウント（表示用）
        
        Args:
            errors: エラーリスト（全ファイル）
            warnings: 警告リスト（全ファイル）
        
        Returns:
            各カテゴリのエラー数の辞書
            {
                'critical': int,
                'high': int,
                'medium': int,
                'low': int,
                'env': int,
                'warnings': int
            }
        """
        # エラー分類定義（_calculate_lint_error_scoreと同じ）
        CRITICAL_CODES = ['E999']  # 構文エラー
        HIGH_CODES = ['E9']  # E9で始まる（インデント等）
        MEDIUM_CODES = ['F4', 'F8', 'F401', 'F841']  # 未使用import/変数
        ENV_CODES = ['F821', 'F823']  # undefined name（環境依存の可能性）
        # それ以外（E501, E701等）はLOW
        
        critical_count = 0
        high_count = 0
        medium_count = 0
        low_count = 0
        env_count = 0
        
        for error in errors:
            code = error.get('code', '')
            message = error.get('message', '').lower()
            
            # 環境依存エラーの判定
            if any(code.startswith(ec) for ec in ENV_CODES):
                # F821/F823でimport関連のエラーは環境問題
                if 'import' in message or 'module' in message:
                    env_count += 1
                    continue
            
            # エラー分類
            if any(code.startswith(cc) for cc in CRITICAL_CODES):
                critical_count += 1
            elif any(code.startswith(hc) for hc in HIGH_CODES):
                high_count += 1
            elif any(code.startswith(mc) for mc in MEDIUM_CODES):
                medium_count += 1
            else:
                low_count += 1
        
        return {
            'critical': critical_count,
            'high': high_count,
            'medium': medium_count,
            'low': low_count,
            'env': env_count,
            'warnings': len(warnings)
        }

    def _detect_placeholders_in_comments(
        self,
        code: str,
        ext: str
    ) -> tuple[int, list[str]]:
        """
        コメント内のプレースホルダーのみを検出
        
        正当なコード（HTML属性のplaceholder、CSS疑似要素::placeholder等）は除外
        
        Args:
            code: ソースコード
            ext: ファイル拡張子（'py', 'js', 'html', 'css'等）
        
        Returns:
            (プレースホルダー数, 検出された行のリスト)
        """
        import re
        
        placeholder_keywords = [
            r'TODO:',
            r'FIXME:',
            r'XXX:',
            r'\bplaceholder\b',  # 単語として
            r'not yet implemented',
            r'coming soon',
            r'implement this',
            r'add.*here when.*is implemented',
        ]
        
        # 除外パターン（正当な使用法）
        exclude_patterns = [
            # === 関数/コード定義パターン ===
            r'def\s+\w*placeholder\w*',     # 関数定義: def generate_placeholder_url
            r'function\s+\w*placeholder\w*', # JS関数定義: function generatePlaceholder
            r'\w*placeholder\w*\s*\(',       # 関数呼び出し: generate_placeholder_url(
            
            # === アセット/リソース生成パターン ===
            r'placeholder.*url',             # placeholder URL生成
            r'placeholder.*image',           # placeholder image生成
            r'generate.*placeholder',        # generate_placeholder系
            
            # === 設計説明パターン（誤検知防止）===
            r'placeholder\s+for\s+\w+',      # "placeholder for sprite", "placeholder for image"
            r'as\s+.*placeholder',           # "as a placeholder", "as simple placeholder"
            r'\(placeholder\s+for',          # "(placeholder for ...)"
            r'placeholder\s+until',          # "placeholder until assets are ready"
            r'simple\s+.*placeholder',       # "simple rectangle placeholder"
            r'temporary\s+placeholder',      # "temporary placeholder"（設計上の説明）
            r'serve\s+as\s+.*placeholder',   # "serve as a placeholder"
            r'act\s+as\s+.*placeholder',     # "act as a placeholder"
            r'used\s+as\s+.*placeholder',    # "used as a placeholder"
            
            # === 提案/可能性コメント ===
            r'use.*placeholder',             # use placeholder（説明コメント）
            r'add\s+a\s+placeholder',        # add a placeholder（提案コメント）
            r'could\s+add.*placeholder',     # could add a placeholder（提案コメント）
            r'may\s+add.*placeholder',       # may add a placeholder（提案コメント）
            r'might\s+add.*placeholder',     # might add a placeholder（提案コメント）
        ]
        
        detected_lines = []
        placeholder_count = 0
        
        # 言語別のコメント抽出
        if ext == 'py':
            # Pythonのコメント: # から行末まで
            comment_pattern = r'#.*$'
            for match in re.finditer(comment_pattern, code, re.MULTILINE | re.IGNORECASE):
                comment_text = match.group(0)
                for keyword in placeholder_keywords:
                    if re.search(keyword, comment_text, re.IGNORECASE):
                        # 除外パターンチェック
                        is_excluded = False
                        for exclude in exclude_patterns:
                            if re.search(exclude, comment_text, re.IGNORECASE):
                                is_excluded = True
                                break
                        
                        if not is_excluded:
                            placeholder_count += 1
                            detected_lines.append(comment_text.strip())
                            break  # 同じコメント行で複数カウントしない
        
        elif ext in ['js', 'jsx', 'ts', 'tsx']:
            # JavaScript/TypeScript
            # 1. 単行コメント: // から行末まで
            single_line_pattern = r'//.*$'
            for match in re.finditer(single_line_pattern, code, re.MULTILINE | re.IGNORECASE):
                comment_text = match.group(0)
                for keyword in placeholder_keywords:
                    if re.search(keyword, comment_text, re.IGNORECASE):
                        # 除外パターンチェック
                        is_excluded = False
                        for exclude in exclude_patterns:
                            if re.search(exclude, comment_text, re.IGNORECASE):
                                is_excluded = True
                                break
                        
                        if not is_excluded:
                            placeholder_count += 1
                            detected_lines.append(comment_text.strip())
                            break
            
            # 2. 複数行コメント: /* ... */
            multi_line_pattern = r'/\*.*?\*/'
            for match in re.finditer(multi_line_pattern, code, re.DOTALL | re.IGNORECASE):
                comment_text = match.group(0)
                for keyword in placeholder_keywords:
                    if re.search(keyword, comment_text, re.IGNORECASE):
                        # 除外パターンチェック
                        is_excluded = False
                        for exclude in exclude_patterns:
                            if re.search(exclude, comment_text, re.IGNORECASE):
                                is_excluded = True
                                break
                        
                        if not is_excluded:
                            placeholder_count += 1
                            # 複数行の場合は最初の行のみ表示
                            first_line = comment_text.split('\n')[0].strip()
                            detected_lines.append(first_line + '...')
                            break
        
        elif ext in ['html', 'htm', 'xml']:
            # HTML/XMLコメント: <!-- ... -->
            comment_pattern = r'<!--.*?-->'
            for match in re.finditer(comment_pattern, code, re.DOTALL | re.IGNORECASE):
                comment_text = match.group(0)
                for keyword in placeholder_keywords:
                    if re.search(keyword, comment_text, re.IGNORECASE):
                        # 除外パターンチェック
                        is_excluded = False
                        for exclude in exclude_patterns:
                            if re.search(exclude, comment_text, re.IGNORECASE):
                                is_excluded = True
                                break
                        
                        if not is_excluded:
                            placeholder_count += 1
                            # 複数行の場合は最初の行のみ表示
                            first_line = comment_text.split('\n')[0].strip()
                            detected_lines.append(first_line + '...' if '\n' in comment_text else comment_text.strip())
                            break
        
        elif ext == 'css':
            # CSSコメント: /* ... */
            comment_pattern = r'/\*.*?\*/'
            for match in re.finditer(comment_pattern, code, re.DOTALL | re.IGNORECASE):
                comment_text = match.group(0)
                for keyword in placeholder_keywords:
                    if re.search(keyword, comment_text, re.IGNORECASE):
                        # 除外パターンチェック
                        is_excluded = False
                        for exclude in exclude_patterns:
                            if re.search(exclude, comment_text, re.IGNORECASE):
                                is_excluded = True
                                break
                        
                        if not is_excluded:
                            placeholder_count += 1
                            first_line = comment_text.split('\n')[0].strip()
                            detected_lines.append(first_line + '...' if '\n' in comment_text else comment_text.strip())
                            break
        
        return placeholder_count, detected_lines

    def _assess_code_quality(
        self, 
        generated_code: Dict[str, str], 
        complexity: str = "medium", 
        goal: str = "",
        lint_result: Optional[Dict[str, Any]] = None
    ) -> Dict[str, float]:
        """
        コード品質を評価(RequirementValidator統合版)
        
        改善点:
        - 依存関係チェッカーと要件検証器を初期化
        - goalパラメータ追加(要件検証用、オプション)
        - 依存関係チェック機能を統合
        - 要件充足度検証を統合
        - 実行可能性チェックを追加
        - 複雑度を考慮した評価基準は既存ロジックを維持
        
        Args:
            generated_code: ファイル名とコードの辞書
            complexity: "simple" | "medium" | "complex"
            goal: 実装目標(要件検証用、オプション)
            
        Returns:
            ファイル名と品質スコアの辞書
        """
        # ==========================================
        # 依存関係チェッカーと要件検証器を初期化
        # ==========================================
        use_advanced_checks = False
        dep_checker = None
        req_validator = None
        
        try:
            from cognix.dependency_checker import DependencyChecker
            from cognix.requirement_validator import RequirementValidator
            dep_checker = DependencyChecker()
            req_validator = RequirementValidator()
            use_advanced_checks = True
        except ImportError:
            dep_checker = None
            req_validator = None
            use_advanced_checks = False
        except Exception as e:
            logger.debug(f"⚠ Advanced checks unavailable: {e}")
            dep_checker = None
            req_validator = None
            use_advanced_checks = False
        
        quality_scores = {}
        all_issues = []
        
        # ==========================================
        # グローバル評価: 要件充足度
        # ==========================================
        requirement_score = 1.0  # デフォルト
        
        if use_advanced_checks and req_validator and goal:
            try:
                requirement_result = req_validator.validate_requirements(goal, generated_code)
                requirement_score = requirement_result['fulfillment_score']
                
                if not requirement_result['is_complete']:
                    all_issues.extend(requirement_result['issues'])
                    logger.debug("[Quality] Requirement Validation Issues:")
                    for issue in requirement_result['issues']:
                        logger.debug(f"[Quality]   {issue}")
            except Exception as e:
                # エラー時はデフォルト値を使用
                logger.debug(f"[Quality] Requirement validation error: {e}")
        
        # ==========================================
        # ファイル別評価
        # ==========================================
        # ==========================================
        # ファイル別評価
        # ==========================================
        for filename, code in generated_code.items():
            ext = filename.split('.')[-1].lower()
            
            # 基本メトリクス
            lines_code = code.split('\n')
            non_empty_lines = [l for l in lines_code if l.strip()]
            
            # コメント行のカウント（複数言語対応）
            comment_lines = []
            for line in lines_code:
                stripped = line.strip()
                if ext in ['py']:
                    if stripped.startswith('#'):
                        comment_lines.append(line)
                elif ext in ['js', 'ts', 'jsx', 'tsx']:
                    if stripped.startswith('//') or stripped.startswith('/*') or stripped.startswith('*'):
                        comment_lines.append(line)
                elif ext in ['html', 'xml']:
                    if '<!--' in stripped:
                        comment_lines.append(line)
                elif ext in ['css']:
                    if stripped.startswith('/*'):
                        comment_lines.append(line)
            
            # 🚨 重要: プレースホルダー検出（コメント内のみ）
            placeholder_score = 1.0
            placeholder_count, detected_lines = self._detect_placeholders_in_comments(code, ext)
            
            if placeholder_count > 0:
                # プレースホルダーがあると大幅減点
                placeholder_score = max(0.1, 1.0 - (placeholder_count * 0.3))
                all_issues.append(f"{filename}: {placeholder_count} placeholder(s) detected")
                logger.debug(f"[Quality] {filename}: Found {placeholder_count} placeholders in comments, score={placeholder_score}")
                # 検出されたコメント行を詳細ログに出力
                for i, line in enumerate(detected_lines[:3], 1):  # 最大3件
                    logger.debug(f"[Quality]   Placeholder {i}: {line}")
                if len(detected_lines) > 3:
                    logger.debug(f"[Quality]   ... and {len(detected_lines) - 3} more")
            
            # 🚨 重要: 空ファイル検出
            if len(non_empty_lines) == 0:
                placeholder_score = 0.0
                all_issues.append(f"{filename}: Empty file")
                logger.debug(f"[Quality] {filename}: Empty file detected")
            
            # 🚨 重要: コメント比率チェック
            comment_ratio_score = 1.0
            if len(non_empty_lines) > 0:
                comment_ratio = len(comment_lines) / len(non_empty_lines)
                if comment_ratio > 0.7:
                    # コメントが70%以上はペナルティ
                    comment_ratio_score = max(0.3, 1.0 - comment_ratio)
                    all_issues.append(f"{filename}: Too many comments ({int(comment_ratio*100)}%)")
                    logger.debug(f"[Quality] {filename}: Comment ratio {int(comment_ratio*100)}%, score={comment_ratio_score}")
            
            # 長さチェックとコメント率は削除（Qualityと直接関係なし）
            # 代わりにLintエラーとComplexityを重視

            
            # 構造チェック(Pythonの場合)
            structure_score = 1.0
            if ext == 'py':
                has_main = 'def ' in code or 'class ' in code
                has_imports = 'import ' in code
                structure_score = 0.5 + (0.25 if has_main else 0) + (0.25 if has_imports else 0)
            
            # 依存関係チェック(新規追加)
            dependency_score = 1.0
            if use_advanced_checks and dep_checker and ext == 'py':
                try:
                    dep_result = dep_checker.check_code_dependencies(code)
                    if dep_result['has_missing']:
                        missing_count = len(dep_result['missing_dependencies'])
                        dependency_score = max(0.5, 1.0 - (missing_count * 0.1))
                        all_issues.append(f"{filename}: {missing_count} missing dependencies")
                except Exception:
                    pass
            
            # Lintエラースコア（エラー種類別の重み付け）
            lint_error_score = 1.0
            if lint_result and lint_result.get('errors'):
                # このファイルのエラーと警告のみをフィルタ
                file_errors = [e for e in lint_result['errors'] if e['file'] == filename]
                file_warnings = [w for w in lint_result.get('warnings', []) if w['file'] == filename]
                
                # エラー種類別のスコア計算（新規メソッド）
                lint_error_score = self._calculate_lint_error_score(file_errors, file_warnings)
                
                if len(file_errors) > 0 or len(file_warnings) > 0:
                    all_issues.append(f"{filename}: {len(file_errors)} errors, {len(file_warnings)} warnings")
            
            # 実行可能性チェック(新規追加)
            executable_score = 1.0
            if ext == 'py':
                try:
                    import ast
                    ast.parse(code)
                    executable_score = 1.0
                except SyntaxError:
                    executable_score = 0.3
                    all_issues.append(f"{filename}: Syntax error detected")
            
            # コード複雑度スコア（新規追加 - McCabe複雑度）
            complexity_score = 1.0
            if ext == 'py':
                try:
                    import radon.complexity as radon_complexity
                    
                    # McCabe複雑度を計算
                    results = radon_complexity.cc_visit(code)
                    if results:
                        avg_complexity = sum(r.complexity for r in results) / len(results)
                        
                        # スコアリング: 1-10: 高品質, 11-20: 中品質, 21+: 低品質
                        if avg_complexity <= 10:
                            complexity_score = 1.0
                        elif avg_complexity <= 20:
                            complexity_score = 0.7
                        else:
                            complexity_score = 0.4
                            all_issues.append(f"{filename}: High complexity ({avg_complexity:.1f})")
                except ImportError:
                    # radonが利用不可の場合はデフォルト値
                    complexity_score = 1.0
                except Exception:
                    complexity_score = 1.0
            
            # 複雑度に応じた重み付け（新指標適用）
            if complexity == "simple":
                weights = {
                    'executable': 0.35,      # 35% - 最重要（構文エラーなし）
                    'lint_error': 0.25,      # 25% - Lintエラー
                    'structure': 0.20,       # 20% - 構造
                    'dependency': 0.15,      # 15% - 依存関係
                    'complexity': 0.05,      # 5% - 複雑度（オプション）
                }
            elif complexity == "medium":
                weights = {
                    'executable': 0.30,      # 30% - 最重要
                    'lint_error': 0.25,      # 25% - Lintエラー
                    'dependency': 0.20,      # 20% - 依存関係
                    'structure': 0.15,       # 15% - 構造
                    'complexity': 0.10,      # 10% - 複雑度
                }
            else:  # complex
                weights = {
                    'executable': 0.25,      # 25% - 最重要
                    'lint_error': 0.25,      # 25% - Lintエラー
                    'dependency': 0.25,      # 25% - 依存関係
                    'complexity': 0.15,      # 15% - 複雑度（重要）
                    'structure': 0.10,       # 10% - 構造
                }
            
            # 総合スコア計算（プレースホルダー・コメント比率を追加）
            file_score = (
                executable_score * weights['executable'] +
                lint_error_score * weights['lint_error'] +
                dependency_score * weights['dependency'] +
                structure_score * weights['structure'] +
                complexity_score * weights['complexity']
            )
            
            # デバッグログ: 中間スコアの詳細
            logger.debug(
                f"[Quality] {filename}: Scores breakdown:\n"
                f"  - executable: {executable_score:.2f} × {weights['executable']:.2f} = {executable_score * weights['executable']:.4f}\n"
                f"  - lint_error: {lint_error_score:.2f} × {weights['lint_error']:.2f} = {lint_error_score * weights['lint_error']:.4f}\n"
                f"  - dependency: {dependency_score:.2f} × {weights['dependency']:.2f} = {dependency_score * weights['dependency']:.4f}\n"
                f"  - structure: {structure_score:.2f} × {weights['structure']:.2f} = {structure_score * weights['structure']:.4f}\n"
                f"  - complexity: {complexity_score:.2f} × {weights['complexity']:.2f} = {complexity_score * weights['complexity']:.4f}\n"
                f"  - RAW total: {file_score:.4f}"
            )
            
            # プレースホルダー・コメント比率による追加ペナルティ
            raw_file_score = file_score  # ペナルティ適用前を保存
            file_score = file_score * placeholder_score * comment_ratio_score
            
            # デバッグログ: ペナルティ適用の詳細
            logger.debug(
                f"[Quality] {filename}: Penalty application:\n"
                f"  - placeholder_score: {placeholder_score:.4f}\n"
                f"  - comment_ratio_score: {comment_ratio_score:.4f}\n"
                f"  - {raw_file_score:.4f} (before) × {placeholder_score:.4f} × {comment_ratio_score:.4f} = {file_score:.4f} (after)"
            )
            
            # Review issuesペナルティ（ファイル単位）
            review_penalty = 0.0
            if hasattr(self, '_zen_summary') and self._zen_summary:
                review_issues_list = self._zen_summary.get("review", {}).get("issues", [])
                if review_issues_list:
                    # このファイルに該当するissueをカウント
                    file_review_issues = [i for i in review_issues_list if i.get('file', '') == filename]
                    file_review_count = len(file_review_issues)
                    if file_review_count > 0:
                        review_penalty = min(file_review_count * 0.02, 0.40)  # 1issue=2%, 最大40%
                        file_score = file_score * (1.0 - review_penalty)
                        logger.debug(
                            f"[Quality] {filename}: Review penalty:\n"
                            f"  - review_issues: {file_review_count}\n"
                            f"  - review_penalty: {review_penalty:.2f} ({int(review_penalty * 100)}%)\n"
                            f"  - FINAL after review: {round(file_score, 2)} ({int(file_score * 100)}%)"
                        )
            
            # 最終デバッグログ
            logger.debug(
                f"[Quality] {filename}: FINAL: {round(file_score, 2)} ({int(file_score * 100)}%)"
            )
            
            # 要件充足度を反映
            #file_score = file_score * requirement_score
            
            quality_scores[filename] = round(file_score, 2)
        
        # グローバルイシューの表示
        if all_issues:
            logger.debug("[Quality] Quality Issues Detected:")
            for issue in all_issues[:5]:  # 最大5件表示
                logger.debug(f"[Quality]   {issue}")
            if len(all_issues) > 5:
                logger.debug(f"[Quality]   ... and {len(all_issues) - 5} more issues")
        
        return quality_scores
    
    def _format_quality_display(
        self,
        base_quality_scores: Dict[str, float],
        lint_errors: int = 0,
        lint_warnings: int = 0,
        review_issues: int = 0
    ) -> Tuple[float, str]:
        """
        品質スコアの総合評価と表示用フォーマット（Phase 1: 表示のみ）
        
        注意: 内部ロジック（閾値チェック等）には影響しない
        
        Args:
            base_quality_scores: ファイルごとの基本品質スコア
            lint_errors: Lintエラー数
            lint_warnings: Lint警告数
            review_issues: Code Review issue数
        
        Returns:
            (overall_score, display_string)
        """
        # 基本品質スコア（ファイル平均）
        # ※ 各ファイルスコアには既にlint/reviewペナルティが適用済み
        if base_quality_scores:
            overall = sum(base_quality_scores.values()) / len(base_quality_scores)
        else:
            overall = 1.0
        
        # フォーマット
        overall_pct = int(overall * 100)
        
        # 残課題がない場合はシンプル表示
        if lint_errors == 0 and lint_warnings == 0 and review_issues == 0:
            return (overall, f"ⓘ Overall Quality:\n  {overall_pct}%")
        
        # 残課題がある場合は情報を表示（ペナルティは既にファイル単位で適用済み）
        remaining_details = []
        if lint_errors > 0:
            remaining_details.append(f"{lint_errors} lint error{'s' if lint_errors > 1 else ''}")
        if lint_warnings > 0:
            remaining_details.append(f"{lint_warnings} lint warning{'s' if lint_warnings > 1 else ''}")
        if review_issues > 0:
            remaining_details.append(f"{review_issues} review issue{'s' if review_issues > 1 else ''}")
        
        remaining_str = ", ".join(remaining_details) + " remaining"
        
        return (overall, f"ⓘ Overall Quality:\n  {overall_pct}% ({remaining_str})")
   
    def _assess_html_quality(self, code: str, complexity: str = "medium") -> float:
        """HTML specific quality assessment"""
        score = 0.0
        
        # HTML基本構造
        if '<!DOCTYPE html>' in code:
            score += 0.15
        
        if '<html' in code and '</html>' in code:
            score += 0.1
        
        if '<head>' in code and '</head>' in code:
            score += 0.1
        
        if '<body>' in code and '</body>' in code:
            score += 0.1
        
        # メタデータ
        if '<meta charset=' in code:
            score += 0.05
        
        if 'viewport' in code:
            score += 0.05
        
        # スタイリング
        if '<style>' in code or 'style=' in code:
            score += 0.1
        
        # スクリプト
        if '<script>' in code:
            score += 0.1
        
        # セマンティック要素(Complex時のみ評価)
        if complexity == "complex":
            semantic_tags = ['<header>', '<nav>', '<main>', '<section>', '<article>', '<footer>']
            if any(tag in code for tag in semantic_tags):
                score += 0.05
        
        return score


    def _assess_css_quality(self, code: str, complexity: str = "medium") -> float:
        """CSS specific quality assessment"""
        score = 0.0
        
        # セレクタの存在
        if '{' in code and '}' in code:
            score += 0.15
        
        # レスポンシブデザイン(Medium以上)
        if complexity in ["medium", "complex"]:
            if '@media' in code:
                score += 0.15
        
        # モダンCSS機能
        if 'flexbox' in code or 'display: flex' in code:
            score += 0.1
        
        if 'grid' in code or 'display: grid' in code:
            score += 0.1
        
        # 変数・カスタムプロパティ(Complex時のみ)
        if complexity == "complex":
            if '--' in code and 'var(' in code:
                score += 0.1
        
        # アニメーション
        if '@keyframes' in code or 'animation:' in code or 'transition:' in code:
            score += 0.1
        
        return score


    def _assess_js_quality(self, code: str, complexity: str = "medium") -> float:
        """JavaScript specific quality assessment"""
        score = 0.0
        
        # 関数定義
        if 'function ' in code or '=>' in code:
            score += 0.15
        
        # モダンJS構文
        if 'const ' in code or 'let ' in code:
            score += 0.1
        
        # エラーハンドリング(Medium以上)
        if complexity in ["medium", "complex"]:
            if 'try' in code and 'catch' in code:
                score += 0.15
        
        # イベントリスナー
        if 'addEventListener' in code or 'onclick' in code:
            score += 0.1
        
        # DOM操作
        if any(method in code for method in ['getElementById', 'querySelector', 'createElement']):
            score += 0.1
        
        # コメント
        if '//' in code or '/*' in code:
            score += 0.1
        
        return score


    def _assess_python_quality(self, code: str, complexity: str = "medium") -> float:
        """Python specific quality assessment(複雑度考慮版)"""
        score = 0.0
        
        # Simple: 基本的な動作のみ評価
        if complexity == "simple":
            # 関数定義(オプション)
            if 'def ' in code:
                score += 0.15
            
            # 基本的な構造があればOK
            if 'print' in code or 'return' in code:
                score += 0.15
            
            # シンプルなコードなので緩く評価
            return score
        
        # Medium: 標準的な品質要求
        if 'def ' in code:
            score += 0.2
        
        if 'try:' in code and 'except' in code:
            score += 0.2
        
        if '"""' in code or "'''" in code:
            score += 0.2
        
        if 'if __name__ == "__main__"' in code:
            score += 0.1
        
        # Complex: より高い品質要求
        if complexity == "complex":
            # 型ヒント
            if '->' in code or ': ' in code:
                score += 0.1
            
            # クラス定義
            if 'class ' in code:
                score += 0.1
        
        return score


    def _assess_compiled_language_quality(self, code: str, complexity: str = "medium") -> float:
        """Quality assessment for compiled languages (Java, C++, etc.)"""
        score = 0.0
        
        # Simple: 基本構造のみ
        if complexity == "simple":
            if 'main' in code:
                score += 0.3
            return score
        
        # 関数/メソッド定義
        if any(keyword in code for keyword in ['void ', 'int ', 'public ', 'private ', 'fn ']):
            score += 0.15
        
        # エラーハンドリング
        if any(keyword in code for keyword in ['try', 'catch', 'throw', 'Result<']):
            score += 0.15
        
        # コメント
        if '//' in code or '/*' in code:
            score += 0.1
        
        # main関数
        if 'main' in code:
            score += 0.1
        
        # クラス定義(Medium以上)
        if complexity in ["medium", "complex"]:
            if any(keyword in code for keyword in ['class ', 'struct ', 'impl ']):
                score += 0.2
        
        return score


    def _assess_generic_quality(self, code: str, complexity: str = "medium") -> float:
        """Generic quality assessment for unknown file types"""
        score = 0.0
        
        # Simple: 緩い基準
        if complexity == "simple":
            if len(code.strip()) > 0:
                score += 0.3
            return score
        
        # コメントの存在
        if any(marker in code for marker in ['//', '/*', '#', '--']):
            score += 0.2
        
        # 構造化されたコード(括弧の使用)
        if '{' in code and '}' in code:
            score += 0.15
        
        # インデント(複数スペースまたはタブ)
        lines = code.split('\n')
        indented_lines = [line for line in lines if line.startswith('    ') or line.startswith('\t')]
        if len(indented_lines) > len(lines) * 0.3:
            score += 0.15
        
        return score

    def _generate_recommendations(self, goal: str, analysis: str, 
                                generated_code: Dict[str, str], 
                                quality_scores: Dict[str, float]) -> List[str]:
        """Generate recommendations based on code quality scores
        
        Only shows recommendations when quality score is below 0.7
        """
        recommendations = []
        
        # 品質スコアに基づく推奨
        for filename, score in quality_scores.items():
            ext = filename.split('.')[-1].lower()
            
            if score < 0.7:
                recommendations.append(f"Consider improving code quality in {filename} (score: {score:.2f})")
                
                # 言語別の具体的改善提案
                if ext == 'html':
                    if score < 0.5:
                        recommendations.append(f"  → Add proper HTML structure, meta tags, and semantic elements")
                elif ext == 'py':
                    if score < 0.5:
                        recommendations.append(f"  → Add functions, error handling, and documentation")
        
        
        return recommendations

    def apply_generated_code(self, result: SemiAutoResult) -> ApplyResult:
        """Apply generated code to files"""
        if not result.success:
            return ApplyResult(
                success=False,
                error="Cannot apply code from failed result"
            )
        
        try:
            applied_files = []
            backup_paths = []
            
            for filename, code in result.generated_code.items():
                file_path = self.workspace_path / filename
                
                # Create backup if file exists
                if file_path.exists() and self.config and self.config.get("auto_backup", True):
                    backup_path = self._create_backup(file_path)
                    if backup_path:
                        backup_paths.append(str(backup_path))
                
                # Write new content
                try:
                    # ===== 親ディレクトリを自動作成 =====
                    file_path.parent.mkdir(parents=True, exist_ok=True)

                    file_path.write_text(code, encoding='utf-8')
                    applied_files.append(str(file_path))
                    
                except Exception as e:
                    return ApplyResult(
                        success=False,
                        error=f"Failed to write {filename}: {str(e)}"
                    )
            
            return ApplyResult(
                success=True,
                applied_files=applied_files,
                backup_paths=backup_paths,
                quality_scores=result.quality_scores
            )
            
        except Exception as e:
            return ApplyResult(
                success=False,
                error=f"Code application failed: {str(e)}"
            )

    def _create_backup(self, file_path: Path) -> Optional[Path]:
        """Create backup of existing file"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_name = f"{file_path.name}.{timestamp}.backup"
            backup_path = self.backup_dir / backup_name
            
            import shutil
            shutil.copy2(file_path, backup_path)
            
            return backup_path
            
        except Exception:
            return None

    def process_message(self, message: str, auto_apply: bool = False) -> Dict[str, Any]:
        """
        Process a message containing code modifications
        
        Args:
            message: Message text containing code blocks
            auto_apply: If True, automatically apply modifications
            
        Returns:
            Dictionary with processing results:
            - modifications: List of extracted modifications
            - preview: Preview of changes
            - applied: Whether changes were applied
            - errors: Any errors encountered
        """
        result = {
            'modifications': [],
            'preview': '',
            'applied': False,
            'errors': [],
            'timestamp': datetime.now().isoformat()
        }
        
        try:
            # Extract modifications from message
            modifications = self.extractor.extract_modifications(message)
            result['modifications'] = modifications
            
            if not modifications:
                result['errors'].append("No valid code modifications found in message")
                return result
                
            # Generate preview
            result['preview'] = self._generate_preview(modifications)
            
            # Apply modifications if requested
            if auto_apply:
                apply_result = self._apply_modifications(modifications)
                result['applied'] = apply_result['success']
                if apply_result['errors']:
                    result['errors'].extend(apply_result['errors'])
                    
            # Log session
            self.session_log.append(result)
            
        except Exception as e:
            result['errors'].append(f"Processing error: {str(e)}")
            
        return result
    
    def _generate_preview(self, modifications: List[Dict[str, Any]]) -> str:
        """Generate a preview of modifications to be applied"""
        preview_lines = []
        preview_lines.append("=== MODIFICATION PREVIEW ===\n")
        
        for i, mod in enumerate(modifications, 1):
            preview_lines.append(f"{i}. {mod['type'].upper()}: {mod['filename']}")
            
            if mod['type'] == 'create':
                preview_lines.append(f"   Creating new file with {len(mod['content'].splitlines())} lines")
                
            elif mod['type'] == 'update':
                line_info = ""
                if 'line_range' in mod:
                    start = mod['line_range']['start']
                    end = mod['line_range']['end']
                    line_info = f" (lines {start}-{end})"
                    
                preview_lines.append(f"   Updating existing file{line_info}")
                preview_lines.append(f"   New content: {len(mod['content'].splitlines())} lines")
                
            elif mod['type'] == 'delete':
                preview_lines.append("   File will be deleted")
                
            preview_lines.append("")
            
        return "\n".join(preview_lines)
    
    def _apply_modifications(self, modifications: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Apply modifications to files"""
        result = {
            'success': True,
            'errors': [],
            'modified_files': [],
            'backed_up_files': []
        }
        
        try:
            for mod in modifications:
                file_result = self._apply_single_modification(mod)
                
                if file_result['success']:
                    result['modified_files'].append(mod['filename'])
                    if file_result.get('backed_up'):
                        result['backed_up_files'].append(mod['filename'])
                else:
                    result['success'] = False
                    result['errors'].extend(file_result['errors'])
                    
        except Exception as e:
            result['success'] = False
            result['errors'].append(f"Application error: {str(e)}")
            
        return result
    
    def _apply_single_modification(self, mod: Dict[str, Any]) -> Dict[str, Any]:
        """Apply a single modification to a file"""
        result = {'success': False, 'errors': [], 'backed_up': False}
        
        try:
            filepath = self.workspace_path / mod['filename']
            
            # Validate file path
            if not self.extractor.validate_file_path(mod['filename']):
                result['errors'].append(f"Invalid file path: {mod['filename']}")
                return result
            
            # ⭐⭐⭐ Safety Guard: Force create→update if file exists ⭐⭐⭐
            # LLMが既存ファイルに対してtype='create'を誤って返した場合の防御
            # これにより、既存ファイルの内容を保持したままupdateが実行される
            if mod['type'] == 'create' and filepath.exists():
                logger.warning(
                    f"[Safety Guard] File '{mod['filename']}' already exists but type='create' specified. "
                    f"Automatically converting to type='update' to preserve existing content."
                )
                # modを辞書コピーして'type'だけ変更
                mod = {**mod, 'type': 'update'}
                
            # Handle different modification types
            if mod['type'] == 'create':
                result = self._create_file(filepath, mod['content'])
                
            elif mod['type'] == 'update':
                result = self._update_file(filepath, mod)
                
            elif mod['type'] == 'delete':
                result = self._delete_file(filepath)
                
            else:
                result['errors'].append(f"Unknown modification type: {mod['type']}")
                
        except Exception as e:
            result['errors'].append(f"File modification error: {str(e)}")
            
        return result
    
    def _create_file(self, filepath: Path, content: str) -> Dict[str, Any]:
        """Create a new file with content"""
        result = {'success': False, 'errors': []}
        
        try:
            # Create parent directories if needed
            filepath.parent.mkdir(parents=True, exist_ok=True)
            
            # Check if file already exists
            if filepath.exists():
                # Backup existing file
                backup_result = self._backup_file(filepath)
                if not backup_result['success']:
                    result['errors'].extend(backup_result['errors'])
                    return result
                result['backed_up'] = True
                
            # Write new content
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write(content)
                
            result['success'] = True
            
        except Exception as e:
            result['errors'].append(f"File creation error: {str(e)}")
            
        return result
    
    def _update_file(self, filepath: Path, mod: Dict[str, Any]) -> Dict[str, Any]:
        """Update an existing file"""
        result = {'success': False, 'errors': []}
        
        try:
            if not filepath.exists():
                result['errors'].append(f"File not found for update: {filepath}")
                return result
                
            # Backup original file
            backup_result = self._backup_file(filepath)
            if not backup_result['success']:
                result['errors'].extend(backup_result['errors'])
                return result
            result['backed_up'] = True
            
            # Read original content
            with open(filepath, 'r', encoding='utf-8') as f:
                original_lines = f.readlines()
                
            # Apply modification
            if 'line_range' in mod:
                # Line-specific update
                start_line = mod['line_range']['start'] - 1  # Convert to 0-based
                end_line = mod['line_range']['end']
                
                new_lines = mod['content'].split('\n')
                # Add newlines except for the last line
                new_lines = [line + '\n' for line in new_lines[:-1]] + [new_lines[-1]]
                
                # Replace specified lines
                updated_lines = (
                    original_lines[:start_line] + 
                    new_lines + 
                    original_lines[end_line:]
                )
            else:
                # Full file replacement
                updated_lines = mod['content'].split('\n')
                updated_lines = [line + '\n' for line in updated_lines[:-1]] + [updated_lines[-1]]
                
            # Write updated content
            with open(filepath, 'w', encoding='utf-8') as f:
                f.writelines(updated_lines)
                
            result['success'] = True
            
        except Exception as e:
            result['errors'].append(f"File update error: {str(e)}")
            
        return result
    
    def _delete_file(self, filepath: Path) -> Dict[str, Any]:
        """Delete a file"""
        result = {'success': False, 'errors': []}
        
        try:
            if not filepath.exists():
                result['errors'].append(f"File not found for deletion: {filepath}")
                return result
                
            # Backup before deletion
            backup_result = self._backup_file(filepath)
            if not backup_result['success']:
                result['errors'].extend(backup_result['errors'])
                return result
            result['backed_up'] = True
            
            # Delete file
            filepath.unlink()
            result['success'] = True
            
        except Exception as e:
            result['errors'].append(f"File deletion error: {str(e)}")
            
        return result
    
    def _backup_file(self, filepath: Path) -> Dict[str, Any]:
        """Create a backup of a file"""
        result = {'success': False, 'errors': []}
        
        try:
            if not filepath.exists():
                result['errors'].append(f"Cannot backup non-existent file: {filepath}")
                return result
                
            # Generate backup filename with timestamp
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_name = f"{filepath.name}.{timestamp}.backup"
            backup_path = self.backup_dir / backup_name
            
            # Copy file to backup location
            import shutil
            shutil.copy2(filepath, backup_path)
            
            result['success'] = True
            result['backup_path'] = str(backup_path)
            
        except Exception as e:
            result['errors'].append(f"Backup error: {str(e)}")
            
        return result
    
    def list_backups(self) -> List[Dict[str, str]]:
        """List available backup files"""
        backups = []
        
        if not self.backup_dir.exists():
            return backups
            
        for backup_file in self.backup_dir.glob("*.backup"):
            # Parse backup filename to extract original name and timestamp
            name_parts = backup_file.name.split('.')
            if len(name_parts) >= 3:
                original_name = '.'.join(name_parts[:-2])
                timestamp = name_parts[-2]
                
                backups.append({
                    'original_name': original_name,
                    'backup_path': str(backup_file),
                    'timestamp': timestamp,
                    'size': backup_file.stat().st_size
                })
                
        return sorted(backups, key=lambda x: x['timestamp'], reverse=True)
    
    def restore_backup(self, backup_path: str, target_path: Optional[str] = None) -> Dict[str, Any]:
        """Restore a file from backup"""
        result = {'success': False, 'errors': []}
        
        try:
            backup_file = Path(backup_path)
            if not backup_file.exists():
                result['errors'].append(f"Backup file not found: {backup_path}")
                return result
                
            if target_path:
                target_file = self.workspace_path / target_path
            else:
                # Extract original filename from backup name
                name_parts = backup_file.name.split('.')
                original_name = '.'.join(name_parts[:-2])
                target_file = self.workspace_path / original_name
                
            # Create target directory if needed
            target_file.parent.mkdir(parents=True, exist_ok=True)
            
            # Copy backup to target location
            import shutil
            shutil.copy2(backup_file, target_file)
            
            result['success'] = True
            result['restored_to'] = str(target_file)
            
        except Exception as e:
            result['errors'].append(f"Restore error: {str(e)}")
            
        return result
    
    def get_session_log(self) -> List[Dict[str, Any]]:
        """Get the current session log"""
        return self.session_log.copy()
    
    def clear_session_log(self):
        """Clear the session log"""
        self.session_log.clear()
    
    def cleanup_temp_files(self):
        """Clean up temporary files"""
        if self.temp_dir and self.temp_dir.exists():
            import shutil
            shutil.rmtree(self.temp_dir)
            self.temp_dir.mkdir(exist_ok=True)
    
    def get_status(self) -> Dict[str, Any]:
        """Get current engine status"""
        return {
            'workspace_path': str(self.workspace_path),
            'backup_dir': str(self.backup_dir),
            'temp_dir': str(self.temp_dir),
            'session_entries': len(self.session_log),
            'available_backups': len(self.list_backups()),
            'workspace_exists': self.workspace_path.exists(),
            'backup_dir_exists': self.backup_dir.exists()
        }

    # ==========================================
    # Phase 2 Auto-Completion Methods
    # ==========================================
    
    def _get_required_phase2_files(
        self, 
        goal: str, 
        project_type: str, 
        generated_files: Dict[str, str]
    ) -> List[str]:
        """
        Phase 2で必須のファイルを判定（改善版）
        
        project_typeとgoalの内容、生成済みファイルの分析を組み合わせて
        必須ファイルを精密に判定します。
        
        Args:
            goal: 実装目標（Goalから直接取得）
            project_type: _detect_project_type()で判定されたタイプ
            generated_files: Phase 1+2で生成された全ファイル
        
        Returns:
            必須ファイルのリスト（パス形式）
        """
        required = []
        goal_lower = goal.lower()
        
        # web_app プロジェクトの場合のみJavaScriptファイルを検査
        if project_type != 'web_app':
            logger.debug(f"[Phase 2 Required] project_type={project_type}, skipping JS check")
            return required
        
        # 条件1: GoalにJavaScript関連キーワードが含まれるか
        js_keywords = [
            'javascript', 'frontend', 'front-end', 'vanilla js',
            'vanilla javascript', 'client-side', 'dom', 'interactive',
            'web app', 'web application', 'html', 'css'
        ]
        has_js_keywords = any(kw in goal_lower for kw in js_keywords)
        
        # 条件2: HTMLファイルが生成されているか
        html_files = [f for f in generated_files.keys() if f.endswith('.html')]
        has_html = len(html_files) > 0
        
        logger.debug(f"[Phase 2 Required] JS keywords: {has_js_keywords}, HTML files: {html_files}")
        
        # 両条件を満たす場合、または明示的にフロントエンドが要求されている場合
        if has_js_keywords or has_html:
            # HTMLファイルから必要なJSファイルパスを抽出
            js_path = self._extract_js_path_from_html(generated_files, html_files)
            
            if js_path:
                required.append(js_path)
                logger.debug(f"[Phase 2 Required] Detected required JS file: {js_path}")
            elif has_html:
                # HTMLはあるがスクリプトパスが不明な場合、デフォルトパスを使用
                default_path = 'static/js/app.js'
                required.append(default_path)
                logger.debug(f"[Phase 2 Required] Using default JS path: {default_path}")
        
        return required

    def _extract_js_path_from_html(
        self, 
        generated_files: Dict[str, str], 
        html_files: List[str]
    ) -> Optional[str]:
        """
        HTMLファイルから参照されているJSファイルパスを抽出
        
        <script src="...">タグを解析し、最も適切なJSファイルパスを返します。
        
        Args:
            generated_files: 生成された全ファイル
            html_files: HTMLファイルのリスト
        
        Returns:
            JSファイルパス（例: 'static/js/app.js'）、見つからない場合はNone
        """
        # 検索対象のJSファイル名パターン
        target_js_names = ['app.js', 'main.js', 'script.js', 'index.js']
        
        for html_file in html_files:
            if html_file not in generated_files:
                continue
            
            html_content = generated_files[html_file]
            
            # <script src="...">タグを抽出
            script_pattern = re.compile(r'<script[^>]+src=["\']([^"\']+)["\']', re.IGNORECASE)
            matches = script_pattern.findall(html_content)
            
            for src in matches:
                # 対象のJSファイル名が含まれているか
                for target in target_js_names:
                    if target in src:
                        # HTMLファイルのディレクトリを考慮してパスを構築
                        html_dir = str(Path(html_file).parent)
                        
                        if html_dir == '.' or html_dir == '':
                            # ルートにHTMLがある場合
                            if src.startswith('/'):
                                js_path = src[1:]  # 先頭の/を除去
                            else:
                                js_path = src
                        else:
                            # サブディレクトリにHTMLがある場合
                            js_path = str(Path(html_dir) / src)
                        
                        # パスの正規化
                        js_path = js_path.replace('\\', '/').replace('//', '/')
                        
                        logger.debug(f"[JS Path Extraction] Found: {js_path} (from {html_file})")
                        return js_path
        
        # index.html の内容を確認して推測
        for html_file in html_files:
            if 'index' in html_file.lower():
                html_dir = str(Path(html_file).parent)
                if html_dir == '.' or html_dir == '':
                    return 'static/js/app.js'
                else:
                    return f'{html_dir}/js/app.js'
        
        return None

    def _find_missing_required_files(
        self, 
        required_files: List[str], 
        generated_files: Dict[str, str]
    ) -> List[str]:
        """
        不足ファイルを検出（改善版スマートマッチング）
        
        パスの正規化と類似ファイル名の許容により、
        より柔軟にファイルの存在を判定します。
        
        Args:
            required_files: 必須ファイルのリスト
            generated_files: 生成されたファイルの辞書
        
        Returns:
            不足しているファイルのリスト
        """
        if not required_files:
            return []
        
        missing = []
        generated_keys = list(generated_files.keys())
        
        # 正規化した生成ファイルリスト
        normalized_generated = {}
        for key in generated_keys:
            normalized = self._normalize_filepath(key)
            normalized_generated[normalized] = key
        
        for required in required_files:
            found = False
            normalized_required = self._normalize_filepath(required)
            
            # 1. 正規化後の完全一致
            if normalized_required in normalized_generated:
                found = True
                logger.debug(f"[Missing Check] ✓ Found (exact): {required}")
            
            # 2. ファイル名のみで一致（パスが異なる場合）
            if not found:
                required_name = Path(required).name
                for gen_key in generated_keys:
                    gen_name = Path(gen_key).name
                    if required_name == gen_name:
                        found = True
                        logger.debug(f"[Missing Check] ✓ Found (name match): {required} as {gen_key}")
                        break
            
            # 3. 類似ファイル名の許容（app.js, main.js, script.js等）
            if not found:
                required_ext = Path(required).suffix
                js_alternatives = ['app.js', 'main.js', 'script.js', 'index.js', 'application.js']
                
                if required_ext == '.js':
                    for gen_key in generated_keys:
                        gen_name = Path(gen_key).name
                        if gen_name in js_alternatives:
                            found = True
                            logger.debug(f"[Missing Check] ✓ Found (alternative): {required} as {gen_key}")
                            break
            
            if not found:
                missing.append(required)
                logger.debug(f"[Missing Check] ✗ Missing: {required}")
        
        return missing
    
    def _normalize_filepath(self, filepath: str) -> str:
        """ファイルパスを正規化（比較用）"""
        # バックスラッシュをスラッシュに変換
        normalized = filepath.replace('\\', '/')
        # 重複スラッシュを除去
        while '//' in normalized:
            normalized = normalized.replace('//', '/')
        # 先頭のスラッシュを除去
        normalized = normalized.lstrip('/')
        # 小文字化
        normalized = normalized.lower()
        return normalized

    def _auto_complete_phase2_files(
        self,
        missing_files: List[str],
        all_files: Dict[str, str],
        goal: str,
        project_type: str,
        detected_language: str
    ) -> Dict[str, str]:
        """
        Phase 2で不足したファイルを自動生成
        
        既存ファイルのコンテキストを活用し、LLMに不足ファイルを生成させます。
        
        Args:
            missing_files: 不足しているファイルのリスト
            all_files: Phase 1+2 で生成された全ファイル
            goal: 元のGoal
            project_type: プロジェクトタイプ
            detected_language: 検出された言語
        
        Returns:
            生成されたファイルの辞書
        """
        completed = {}
        
        for missing_file in missing_files:
            logger.debug(f"[Phase 2 Auto-Complete] Generating: {missing_file}")
            
            # ファイルの説明を取得
            file_description = self._get_file_description_for_completion(missing_file, project_type)
            
            # ⚠️ CRITICAL: existing_files を設定
            # ComplexityAssessor は初期化時にディスクをスキャンするが、
            # Phase 1/Phase 2 で生成されたファイルはメモリ内にのみ存在する。
            # そのため、明示的に existing_files を更新する必要がある。
            if hasattr(self, '_complexity_assessor'):
                self._complexity_assessor.existing_files = all_files.copy()
                logger.debug(f"[Phase 2 Auto-Complete] Set existing_files: {len(all_files)} files")
            
            # completion_goal を構築
            completion_goal = f"""Generate the missing file: {missing_file}

File purpose: {file_description}

Project type: {project_type}
Language: {detected_language}

CRITICAL REQUIREMENTS:
1. Generate ONLY this file - do NOT regenerate any other files
2. The file must integrate with the existing application architecture
3. You will see the existing files in context - use them to understand the project structure

For JavaScript frontend files (app.js, main.js, script.js):
- Use vanilla JavaScript (no frameworks like React, Vue, Angular)
- Use Fetch API for backend API calls
- Implement complete DOM manipulation and event listeners
- Handle all CRUD operations (Create, Read, Update, Delete)
- Add proper error handling with try-catch
- Include loading indicators and user feedback
- Match the API endpoints defined in the backend files

MANDATORY IMPLEMENTATION:
- This file MUST be complete and functional
- NO placeholder comments like "// TODO: implement"
- NO incomplete functions
- FULL implementation of all required features

Generate complete, production-ready code for {missing_file} only."""
            
            try:
                # ⚠️ CRITICAL: force_filename を使用
                # force_filename を指定することで：
                # 1. Multi-File Detection がスキップされる（4305-4308行の既存条件分岐）
                # 2. LLMが返したファイル名を無視して、force_filenameで保存される
                # 3. 単一ファイル生成が保証される
                result = self._generate_code_implementation(
                    goal=completion_goal,
                    analysis=f"Auto-completing missing file: {missing_file}",
                    plan=f"Generate {missing_file} to complete Phase 2",
                    complexity='simple',
                    skip_validation=True,  # post-validation は不要
                    skip_multi_stage=True,  # 多段階生成をスキップ
                    force_filename=missing_file
                )
                
                if result and missing_file in result:
                    completed[missing_file] = result[missing_file]
                    # ⚠️ 重要: 次のファイル生成時にこのファイルがコンテキストに含まれるよう、
                    # all_files を更新
                    all_files[missing_file] = result[missing_file]
                    logger.debug(f"[Phase 2 Auto-Complete] ✓ Successfully generated: {missing_file}")
                else:
                    logger.warning(f"[Phase 2 Auto-Complete] ✗ Failed to generate: {missing_file}")
                    if result:
                        logger.warning(f"[Phase 2 Auto-Complete]   Result keys: {list(result.keys())}")
                    else:
                        logger.warning(f"[Phase 2 Auto-Complete]   Result was None or empty")
            
            except Exception as e:
                logger.error(f"[Phase 2 Auto-Complete] ✗ Exception while generating {missing_file}: {e}")
                import traceback
                logger.error(f"[Phase 2 Auto-Complete]   Traceback: {traceback.format_exc()}")
        
        return completed

    def _get_file_description_for_completion(self, missing_file: str, project_type: str) -> str:
        """
        ファイルの説明を返す（Auto-Completion用）
        
        Args:
            missing_file: ファイル名/パス
            project_type: プロジェクトタイプ
        
        Returns:
            ファイルの説明文
        """
        file_name = Path(missing_file).name.lower()
        
        # JavaScriptファイルの説明
        js_descriptions = {
            'app.js': 'Frontend JavaScript application - handles all UI interactions, API calls via Fetch API, DOM manipulation, event listeners, form submissions, CRUD operations, toast notifications, and loading indicators',
            'main.js': 'Main JavaScript entry point - initializes the application, sets up event listeners, and coordinates modules',
            'script.js': 'JavaScript implementation - contains all client-side logic and user interactions',
            'index.js': 'JavaScript index module - exports and coordinates other modules',
            'application.js': 'Application JavaScript - main application logic and UI handling',
        }
        
        if file_name in js_descriptions:
            return js_descriptions[file_name]
        
        # 拡張子による汎用説明
        ext = Path(missing_file).suffix.lower()
        generic_descriptions = {
            '.js': 'JavaScript module implementing client-side functionality',
            '.ts': 'TypeScript module implementing typed client-side functionality',
            '.css': 'Stylesheet for visual styling',
            '.html': 'HTML document for user interface',
            '.py': 'Python module implementing server-side functionality',
        }
        
        if ext in generic_descriptions:
            return generic_descriptions[ext]
        
        # デフォルト
        return f'{missing_file} implementation'

    # ==========================================
    # 共通LLM呼び出しメソッド
    # ==========================================
    
    def _call_llm_api(self, prompt: str, system_prompt: str = None, max_tokens: int = 8000) -> Optional[str]:
        """
        LLM APIを呼び出す共通メソッド
        
        Args:
            prompt: プロンプト文字列
            system_prompt: システムプロンプト（オプション）
            max_tokens: 最大トークン数
        
        Returns:
            レスポンステキスト、失敗時はNone
        """
        if not self.llm_manager:
            logger.debug("[LLM API] llm_manager not available")
            return None
        
        try:
            if system_prompt is None:
                system_prompt = "You are an expert software engineer. Generate clean, working code."
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt=system_prompt,
                max_tokens=max_tokens
            )
            
            if hasattr(response, 'content'):
                return response.content
            elif isinstance(response, str):
                return response
            else:
                logger.debug("[LLM API] Unexpected response type")
                return None
                
        except Exception as e:
            logger.debug(f"[LLM API] Error: {e}")
            return None

    # ==========================================
    # G-1: HTML-CSS クラス整合性検証
    # ==========================================
    
    def _validate_html_css_class_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[str]:
        """
        HTMLで使用されているがCSSに未定義のクラスを検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            CSSに未定義のクラス名リスト
        """
        import re
        
        # 1. HTMLから全クラスを抽出
        html_classes = set()
        for filepath, content in generated_files.items():
            if filepath.endswith(('.html', '.htm')):
                # class="xxx yyy zzz" を抽出
                matches = re.findall(r'class=["\']([^"\']*)["\']', content, re.IGNORECASE)
                for match in matches:
                    # スペース区切りで分割
                    html_classes.update(match.split())
        
        if not html_classes:
            logger.debug("[G-1] No HTML classes found")
            return []
        
        # 2. CSSから全セレクタを抽出
        css_classes = set()
        for filepath, content in generated_files.items():
            if filepath.endswith('.css'):
                # .classname を抽出（擬似クラス :hover 等は除外）
                matches = re.findall(r'\.([a-zA-Z_-][a-zA-Z0-9_-]*)(?=[^a-zA-Z0-9_-]|$)', content)
                css_classes.update(matches)
        
        # 3. 差分を検出
        missing = html_classes - css_classes
        
        # 4. 一般的なユーティリティクラス（外部CSSフレームワーク想定）は除外
        utility_prefixes = ('sr-', 'visually-', 'aria-')
        missing = {c for c in missing if not any(c.startswith(p) for p in utility_prefixes)}
        
        if missing:
            logger.debug(f"[G-1] Found {len(missing)} missing CSS classes: {list(missing)[:10]}...")
        else:
            logger.debug("[G-1] All HTML classes are defined in CSS")
        
        return list(missing)
    
    def _auto_complete_missing_css_classes(
        self,
        missing_classes: List[str],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        不足CSSクラスをLLMで生成してstyles.cssに追記
        
        Args:
            missing_classes: 不足しているCSSクラス名のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not missing_classes:
            return generated_files
        
        # CSSファイルを特定
        css_files = [f for f in generated_files.keys() if f.endswith('.css')]
        if not css_files:
            logger.debug("[G-1] No CSS file found, skipping auto-completion")
            return generated_files
        
        css_file = css_files[0]  # 最初のCSSファイルを使用
        existing_css = generated_files[css_file]
        
        # HTMLコンテキストを抽出（クラスの使用状況を把握）
        html_context = ""
        for filepath, content in generated_files.items():
            if filepath.endswith(('.html', '.htm')):
                # クラスが使われている要素を抽出
                import re
                for cls in missing_classes[:10]:  # 最大10クラスのコンテキスト
                    pattern = rf'<[^>]*class=["\'][^"\']*\b{re.escape(cls)}\b[^"\']*["\'][^>]*>'
                    matches = re.findall(pattern, content)
                    if matches:
                        html_context += f"\n/* {cls} is used in: {matches[0][:100]} */\n"
        
        # LLMプロンプト
        prompt = f"""Generate CSS definitions for these missing classes that are used in HTML but not defined in CSS:

Missing classes: {missing_classes}

{html_context}

Existing CSS structure (for reference):
{existing_css[:2000]}

Requirements:
1. Generate ONLY the CSS rules for the missing classes
2. Match the existing CSS style (colors, spacing, fonts)
3. Include reasonable default styles:
   - For buttons (btn, btn-*): padding, border-radius, cursor, hover states
   - For forms (form-*): proper spacing, borders, focus states
   - For layout (container, *-section): width, margin, padding
   - For icons (stat-icon, *-icon): proper sizing (width, height), display: flex, alignment
   - For modals (modal, modal-*): positioning, overlay, z-index
   - For tables (table-*, products-*): borders, spacing, alignment
4. Do NOT include any explanation, just CSS code
5. Use consistent naming with existing styles

Generate CSS:"""

        logger.debug(f"[G-1] Generating CSS for {len(missing_classes)} missing classes...")
        
        # LLM呼び出し
        try:
            result = self._generate_code_implementation(
                goal=prompt,
                complexity="simple",
                skip_validation=True,
                skip_multi_stage=True,
                force_filename=None
            )
            
            if result:
                # 生成されたCSSを抽出
                new_css = ""
                if isinstance(result, dict):
                    for content in result.values():
                        new_css = content
                        break
                elif isinstance(result, str):
                    new_css = result
                
                # CSSコードのみを抽出（マークダウン等を除去）
                import re
                # ```css ... ``` ブロックを抽出
                css_match = re.search(r'```(?:css)?\s*\n(.*?)```', new_css, re.DOTALL)
                if css_match:
                    new_css = css_match.group(1)
                
                # 既存CSSに追記
                if new_css.strip():
                    generated_files[css_file] = existing_css + "\n\n/* ========================================\n   Auto-generated: Missing CSS Classes (G-1)\n   ======================================== */\n" + new_css.strip()
                    logger.debug(f"[G-1] ✅ Added {len(new_css)} chars of CSS to {css_file}")
                else:
                    logger.debug("[G-1] ⚠ Generated CSS was empty")
            else:
                logger.debug("[G-1] ⚠ LLM returned no result")
                
        except Exception as e:
            logger.debug(f"[G-1] ⚠ Error generating CSS: {e}")
        
        return generated_files

    # ==========================================
    # G-2: HTML-JS ID整合性検証
    # ==========================================
    
    def _validate_html_js_id_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[str]:
        """
        JSで参照されているがHTMLに存在しないIDを検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            HTMLに存在しないID名リスト
        """
        import re
        
        # 1. JSから参照されているIDを抽出
        js_ids = set()
        for filepath, content in generated_files.items():
            if filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                # getElementById('xxx') を抽出
                matches = re.findall(r'getElementById\(["\']([^"\']+)["\']\)', content)
                js_ids.update(matches)
                
                # querySelector('#xxx') を抽出
                matches = re.findall(r'querySelector\(["\']#([^"\'#\s\[]+)["\']', content)
                js_ids.update(matches)
                
                # document.forms.xxx や document.xxx を抽出（一般的なパターン）
                matches = re.findall(r'document\.(?:forms\.)?([a-zA-Z_][a-zA-Z0-9_]*)\b', content)
                # これらはIDでない可能性が高いのでスキップ
        
        if not js_ids:
            logger.debug("[G-2] No JS ID references found")
            return []
        
        # 2. HTMLから全IDを抽出
        html_ids = set()
        for filepath, content in generated_files.items():
            if filepath.endswith(('.html', '.htm')):
                # id="xxx" を抽出
                matches = re.findall(r'id=["\']([^"\']+)["\']', content, re.IGNORECASE)
                html_ids.update(matches)
        
        # 3. 差分を検出
        missing = js_ids - html_ids
        
        if missing:
            logger.debug(f"[G-2] Found {len(missing)} missing HTML IDs: {list(missing)[:10]}...")
        else:
            logger.debug("[G-2] All JS-referenced IDs exist in HTML")
        
        return list(missing)
    
    def _auto_complete_missing_html_ids(
        self,
        missing_ids: List[str],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        JSで参照されているが存在しないIDをHTMLに追加
        
        Args:
            missing_ids: 不足しているID名のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not missing_ids:
            return generated_files
        
        # HTMLファイルを特定
        html_files = [f for f in generated_files.keys() if f.endswith(('.html', '.htm'))]
        if not html_files:
            logger.debug("[G-2] No HTML file found, skipping auto-completion")
            return generated_files
        
        html_file = html_files[0]  # 最初のHTMLファイルを使用
        existing_html = generated_files[html_file]
        
        # JSコンテキストを抽出（IDの使用状況を把握）
        js_context = ""
        for filepath, content in generated_files.items():
            if filepath.endswith(('.js', '.jsx')):
                import re
                for id_name in missing_ids[:15]:  # 最大15個のコンテキスト
                    # IDが使われている行を抽出
                    pattern = rf'.*getElementById\(["\']({re.escape(id_name)})["\'].*'
                    matches = re.findall(pattern, content)
                    if matches:
                        # 周辺コードを取得
                        for line in content.split('\n'):
                            if id_name in line:
                                js_context += f"// {id_name}: {line.strip()[:100]}\n"
                                break
        
        # LLMプロンプト
        prompt = f"""Fix the HTML file by adding missing elements with these IDs that are referenced by JavaScript but don't exist in the HTML:

Missing IDs: {missing_ids}

JavaScript usage context:
{js_context}

Current HTML:
{existing_html}

Requirements:
1. Add the missing elements with the correct IDs in appropriate locations
2. Infer the element type from the ID name and JS usage:
   - *Btn, *Button → <button>
   - *Input, *Field → <input>
   - *Select, *Filter → <select>
   - *Container, *Section → <div>
   - *Table, *List → appropriate container
   - *Modal → modal structure
   - *Form → <form>
   - *Page, *Count → <span>
3. Place elements in logical locations based on their purpose
4. Maintain the existing HTML structure and styling
5. Return the COMPLETE updated HTML file

Generate the complete fixed HTML:"""

        logger.debug(f"[G-2] Generating HTML with {len(missing_ids)} missing IDs...")
        
        # LLM呼び出し
        try:
            result = self._generate_code_implementation(
                goal=prompt,
                complexity="medium",
                skip_validation=True,
                skip_multi_stage=True,
                force_filename=html_file
            )
            
            if result:
                new_html = ""
                if isinstance(result, dict):
                    new_html = result.get(html_file, "")
                    if not new_html:
                        for content in result.values():
                            new_html = content
                            break
                elif isinstance(result, str):
                    new_html = result
                
                # HTMLコードを抽出（マークダウン等を除去）
                import re
                html_match = re.search(r'```(?:html)?\s*\n(.*?)```', new_html, re.DOTALL)
                if html_match:
                    new_html = html_match.group(1)
                
                # 新しいHTMLが有効か検証
                if new_html.strip() and '<html' in new_html.lower() or '<!doctype' in new_html.lower() or '<body' in new_html.lower():
                    # 追加されたIDを確認
                    added_ids = []
                    for id_name in missing_ids:
                        if f'id="{id_name}"' in new_html or f"id='{id_name}'" in new_html:
                            added_ids.append(id_name)
                    
                    if added_ids:
                        generated_files[html_file] = new_html.strip()
                        logger.debug(f"[G-2] ✅ Added {len(added_ids)} IDs to {html_file}: {added_ids[:5]}...")
                    else:
                        logger.debug("[G-2] ⚠ Generated HTML doesn't contain the missing IDs")
                else:
                    logger.debug("[G-2] ⚠ Generated HTML appears invalid, keeping original")
            else:
                logger.debug("[G-2] ⚠ LLM returned no result")
                
        except Exception as e:
            logger.debug(f"[G-2] ⚠ Error generating HTML: {e}")
        
        return generated_files

    # ==========================================
    # G-3: Python環境-パッケージ互換性検証
    # ==========================================
    
    def _validate_python_package_compatibility(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, str]]:
        """
        README記載のPythonバージョンとrequirements.txt内のパッケージの互換性を検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            互換性問題のリスト [{package, current_version, issue, fix}]
        """
        import re
        import sys
        
        # 1. システム環境からPythonバージョンを取得（優先）
        # ユーザーの実行環境で動作する必要があるため、システム環境を優先
        python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
        logger.debug(f"[G-3] System Python version: {python_version}")
        
        # READMEからも参考として取得（ログ用）
        readme_python_version = None
        for filepath, content in generated_files.items():
            if filepath.lower().endswith(('readme.md', 'readme.txt', 'readme')):
                # Python 3.13, Python 3.11+, Python 3.10+ 等を検出
                match = re.search(r'Python\s*(3\.1[0-9])', content, re.IGNORECASE)
                if match:
                    readme_python_version = match.group(1)
                    break
        
        if readme_python_version:
            logger.debug(f"[G-3] README Python version: {readme_python_version}")
        
        logger.debug(f"[G-3] Using Python version for compatibility check: {python_version}")
        
        # 2. requirements.txtからパッケージを抽出
        requirements_content = None
        for filepath, content in generated_files.items():
            if filepath.lower() == 'requirements.txt':
                requirements_content = content
                break
        
        if not requirements_content:
            logger.debug("[G-3] No requirements.txt found")
            return []
        
        # パッケージとバージョンを抽出
        packages = {}
        for line in requirements_content.split('\n'):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            # package==version, package>=version, package~=version
            match = re.match(r'^([a-zA-Z0-9_-]+)\s*[=<>~!]+\s*([0-9.]+)', line)
            if match:
                packages[match.group(1).lower()] = match.group(2)
        
        logger.debug(f"[G-3] Found {len(packages)} packages in requirements.txt")
        
        # 3. 既知の互換性問題をチェック
        # Python 3.13との互換性問題データベース
        COMPATIBILITY_ISSUES = {
            '3.13': {
                'sqlalchemy': {
                    'min_version': '2.0.36',
                    'issue': 'SQLAlchemy < 2.0.36 has __firstlineno__/__static_attributes__ conflict with Python 3.13',
                    'fix': '2.0.36'
                },
                'flask-sqlalchemy': {
                    'min_version': '3.1.1',
                    'issue': 'Older versions may have compatibility issues with Python 3.13',
                    'fix': '3.1.1'
                },
                'numpy': {
                    'min_version': '1.26.0',
                    'issue': 'NumPy < 1.26.0 not compatible with Python 3.13',
                    'fix': '1.26.4'
                },
                'pandas': {
                    'min_version': '2.1.0',
                    'issue': 'Pandas < 2.1.0 not compatible with Python 3.13',
                    'fix': '2.2.0'
                },
                'pydantic': {
                    'min_version': '2.5.0',
                    'issue': 'Pydantic < 2.5.0 may have issues with Python 3.13',
                    'fix': '2.6.0'
                },
            },
            '3.12': {
                'sqlalchemy': {
                    'min_version': '2.0.20',
                    'issue': 'SQLAlchemy < 2.0.20 has issues with Python 3.12',
                    'fix': '2.0.36'
                },
            }
        }
        
        issues = []
        
        # Pythonバージョンに対応する互換性チェック
        version_key = python_version if python_version in COMPATIBILITY_ISSUES else None
        
        if version_key:
            known_issues = COMPATIBILITY_ISSUES[version_key]
            
            for pkg_name, pkg_version in packages.items():
                if pkg_name in known_issues:
                    issue_info = known_issues[pkg_name]
                    min_version = issue_info['min_version']
                    
                    # バージョン比較
                    if self._compare_versions(pkg_version, min_version) < 0:
                        issues.append({
                            'package': pkg_name,
                            'current_version': pkg_version,
                            'min_version': min_version,
                            'issue': issue_info['issue'],
                            'fix': issue_info['fix']
                        })
                        logger.debug(f"[G-3] Incompatibility found: {pkg_name}=={pkg_version} (requires >= {min_version})")
        
        if issues:
            logger.debug(f"[G-3] Found {len(issues)} compatibility issues")
        else:
            logger.debug("[G-3] All packages are compatible with Python version")
        
        return issues
    
    def _compare_versions(self, version1: str, version2: str) -> int:
        """
        バージョン文字列を比較
        
        Returns:
            -1: version1 < version2
             0: version1 == version2
             1: version1 > version2
        """
        def normalize(v):
            return [int(x) for x in re.sub(r'[^0-9.]', '', v).split('.')]
        
        import re
        v1_parts = normalize(version1)
        v2_parts = normalize(version2)
        
        # パディング
        max_len = max(len(v1_parts), len(v2_parts))
        v1_parts.extend([0] * (max_len - len(v1_parts)))
        v2_parts.extend([0] * (max_len - len(v2_parts)))
        
        for a, b in zip(v1_parts, v2_parts):
            if a < b:
                return -1
            elif a > b:
                return 1
        return 0
    
    def _auto_fix_package_compatibility(
        self,
        issues: List[Dict[str, str]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        パッケージ互換性問題を自動修正
        
        Args:
            issues: 互換性問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # requirements.txtを特定
        req_file = None
        req_content = None
        for filepath, content in generated_files.items():
            if filepath.lower() == 'requirements.txt':
                req_file = filepath
                req_content = content
                break
        
        if not req_file or not req_content:
            logger.debug("[G-3] No requirements.txt found for fixing")
            return generated_files
        
        # 各問題を修正
        import re
        updated_content = req_content
        
        for issue in issues:
            pkg_name = issue['package']
            fix_version = issue['fix']
            
            # パッケージ行を検索して置換
            # 大文字小文字を区別しないパターン
            pattern = rf'^({re.escape(pkg_name)})\s*[=<>~!]+\s*[0-9.]+(.*)$'
            replacement = rf'\1=={fix_version}\2'
            
            updated_content = re.sub(
                pattern,
                replacement,
                updated_content,
                flags=re.MULTILINE | re.IGNORECASE
            )
            
            logger.debug(f"[G-3] Fixed: {pkg_name} -> {fix_version}")
        
        if updated_content != req_content:
            generated_files[req_file] = updated_content
            logger.debug(f"[G-3] ✅ Updated requirements.txt with {len(issues)} fixes")
        
        return generated_files

    # ==========================================
    # G-4: Pythonシンボルレベルimport整合性検証
    # ==========================================
    
    def _validate_python_symbol_imports(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, str]]:
        """
        Pythonファイル間のシンボルレベルimport整合性を検証
        
        from X import Y の形式で、モジュールXにシンボルYが存在するか確認
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            不整合のリスト [{importer, module, symbol, available_symbols}]
        """
        import re
        
        issues = []
        
        # 1. 生成されたPythonファイルのモジュール名マッピングを作成
        # filepath → module_name
        module_map = {}
        for filepath in generated_files.keys():
            if not filepath.endswith('.py'):
                continue
            # models/product.py → models.product, database.py → database
            module_name = filepath.replace('/', '.').replace('\\', '.').replace('.py', '')
            # __init__.py は特別処理
            if module_name.endswith('.__init__'):
                module_name = module_name.replace('.__init__', '')
            module_map[module_name] = filepath
            # 短縮名も登録（database.py → database）
            short_name = filepath.replace('.py', '').split('/')[-1].split('\\')[-1]
            if short_name != '__init__':
                module_map[short_name] = filepath
        
        logger.debug(f"[G-4] Module map: {list(module_map.keys())}")
        
        # 2. 各ファイルのシンボル定義を抽出
        module_symbols = {}
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            symbols = self._extract_module_symbols(content)
            module_symbols[filepath] = symbols
            logger.debug(f"[G-4] {filepath}: {len(symbols)} symbols")
        
        # 3. 各ファイルの from X import Y を解析
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # from X import Y, Z パターンを抽出
            # from database import get_db, init_db
            # from models.product import Product
            # from .item_schema import ItemSchema  ← 相対インポート対応
            # from ..models import User  ← 親ディレクトリ相対インポート対応
            pattern = r'^from\s+([.a-zA-Z0-9_]+)\s+import\s+(.+)$'
            
            for line in content.split('\n'):
                line = line.strip()
                match = re.match(pattern, line)
                if not match:
                    continue
                
                module_name = match.group(1)
                imports_str = match.group(2)
                
                # コメントを除去
                if '#' in imports_str:
                    imports_str = imports_str.split('#')[0]
                
                # カッコ内の継続行は簡略化のため無視
                if '(' in imports_str and ')' not in imports_str:
                    continue
                
                # インポートされるシンボルを抽出
                imports_str = imports_str.replace('(', '').replace(')', '')
                imported_symbols = [s.strip().split(' as ')[0].strip() for s in imports_str.split(',')]
                imported_symbols = [s for s in imported_symbols if s and s != '*']
                
                # モジュールが生成ファイル内に存在するか確認
                target_filepath = None
                
                # 相対インポートの処理 (from .X import ... または from ..X import ...)
                if module_name.startswith('.'):
                    # インポート元ファイルのディレクトリを取得
                    if '/' in filepath:
                        importer_dir = '/'.join(filepath.split('/')[:-1])
                    elif '\\' in filepath:
                        importer_dir = '\\'.join(filepath.split('\\')[:-1])
                    else:
                        importer_dir = ''
                    
                    # 相対パスを解決
                    # from .item_schema import ... → 同一ディレクトリの item_schema.py
                    # from ..models import ... → 親ディレクトリの models/__init__.py
                    relative_module = module_name.lstrip('.')
                    parent_count = len(module_name) - len(relative_module)
                    
                    # 親ディレクトリに移動
                    path_parts = importer_dir.split('/') if '/' in importer_dir else importer_dir.split('\\')
                    path_parts = [p for p in path_parts if p]  # 空文字除去
                    
                    for _ in range(parent_count - 1):  # .は同一ディレクトリなので-1
                        if path_parts:
                            path_parts.pop()
                    
                    # ターゲットパスを構築
                    if relative_module:
                        relative_parts = relative_module.replace('.', '/').split('/')
                        target_path = '/'.join(path_parts + relative_parts) + '.py'
                    else:
                        # from . import X の場合は __init__.py
                        target_path = '/'.join(path_parts) + '/__init__.py' if path_parts else '__init__.py'
                    
                    # ファイルが存在するか確認
                    if target_path in generated_files:
                        target_filepath = target_path
                    else:
                        # __init__.py の場合を試す
                        init_path = target_path.replace('.py', '/__init__.py')
                        if init_path in generated_files:
                            target_filepath = init_path
                    
                    if target_filepath:
                        logger.debug(f"[G-4] Resolved relative import: {filepath} -> {module_name} -> {target_filepath}")
                else:
                    # 絶対インポートの処理（従来ロジック）
                    for mod_name, mod_path in module_map.items():
                        if module_name == mod_name or module_name.endswith('.' + mod_name):
                            target_filepath = mod_path
                            break
                
                if not target_filepath:
                    # 外部モジュール（標準ライブラリ、サードパーティ）はスキップ
                    continue
                
                # ターゲットモジュールのシンボルを取得
                available_symbols = module_symbols.get(target_filepath, set())
                
                # 各シンボルの存在確認
                for symbol in imported_symbols:
                    if symbol not in available_symbols:
                        issues.append({
                            'importer': filepath,
                            'module': module_name,
                            'module_path': target_filepath,
                            'symbol': symbol,
                            'available_symbols': list(available_symbols)[:10],  # 最大10個
                            'import_line': line
                        })
                        logger.debug(f"[G-4] Missing symbol: {filepath} imports '{symbol}' from {module_name}, but not found in {target_filepath}")
        
        if issues:
            logger.debug(f"[G-4] Found {len(issues)} symbol import issues")
        else:
            logger.debug("[G-4] All symbol imports are valid")
        
        return issues
    
    def _extract_module_symbols(self, content: str) -> set:
        """
        Pythonモジュールから定義されているシンボルを抽出
        
        Args:
            content: Pythonファイルの内容
        
        Returns:
            シンボル名のセット (関数名、クラス名、変数名)
        """
        import re
        
        symbols = set()
        
        # 関数定義: def function_name(
        func_pattern = r'^def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\('
        for match in re.finditer(func_pattern, content, re.MULTILINE):
            symbols.add(match.group(1))
        
        # クラス定義: class ClassName
        class_pattern = r'^class\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[:\(]'
        for match in re.finditer(class_pattern, content, re.MULTILINE):
            symbols.add(match.group(1))
        
        # トップレベル変数代入: VARIABLE = value (大文字の定数)
        const_pattern = r'^([A-Z][A-Z0-9_]*)\s*='
        for match in re.finditer(const_pattern, content, re.MULTILINE):
            symbols.add(match.group(1))
        
        # トップレベル変数代入: variable = value (小文字も含む、インデントなし)
        var_pattern = r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*='
        for match in re.finditer(var_pattern, content, re.MULTILINE):
            # インデントされていない行のみ（トップレベル）
            symbols.add(match.group(1))
        
        # __all__ で明示的にエクスポートされているシンボル
        all_pattern = r'__all__\s*=\s*\[(.*?)\]'
        all_match = re.search(all_pattern, content, re.DOTALL)
        if all_match:
            all_content = all_match.group(1)
            exported = re.findall(r'["\']([a-zA-Z_][a-zA-Z0-9_]*)["\']', all_content)
            symbols.update(exported)
        
        return symbols
    
    def _auto_fix_symbol_imports(
        self,
        issues: List[Dict[str, str]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        シンボルimport不整合を自動修正
        
        方針: 不足しているシンボルをターゲットモジュールに追加する
              （エイリアス関数として既存の類似関数を参照）
        
        Args:
            issues: 不整合のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # モジュールごとに不足シンボルをグループ化
        module_fixes = {}
        for issue in issues:
            module_path = issue['module_path']
            if module_path not in module_fixes:
                module_fixes[module_path] = []
            module_fixes[module_path].append(issue)
        
        # 各モジュールに不足シンボルを追加
        for module_path, module_issues in module_fixes.items():
            if module_path not in generated_files:
                continue
            
            content = generated_files[module_path]
            additions = []
            
            for issue in module_issues:
                symbol = issue['symbol']
                available = issue['available_symbols']
                
                # 類似シンボルを探す（エイリアス候補）
                alias_target = None
                
                # get_db → get_session, get_db_session などを探す
                for avail in available:
                    # 完全一致の接頭辞/接尾辞
                    if symbol in avail or avail in symbol:
                        alias_target = avail
                        break
                    # get_X と get_X_session の関係
                    if symbol.replace('_', '') in avail.replace('_', ''):
                        alias_target = avail
                        break
                
                if alias_target:
                    # エイリアス関数を追加
                    additions.append(f"\n\n# Auto-generated alias for compatibility (G-4)\ndef {symbol}(*args, **kwargs):\n    \"\"\"Alias for {alias_target}\"\"\"\n    return {alias_target}(*args, **kwargs)\n")
                    logger.debug(f"[G-4] Adding alias: {symbol} -> {alias_target} in {module_path}")
                else:
                    # 類似シンボルがない場合はスタブ関数を追加
                    additions.append(f"\n\n# Auto-generated stub (G-4) - TODO: Implement\ndef {symbol}(*args, **kwargs):\n    \"\"\"Auto-generated stub. Implement this function.\"\"\"\n    raise NotImplementedError('{symbol} is not implemented')\n")
                    logger.debug(f"[G-4] Adding stub: {symbol} in {module_path}")
            
            if additions:
                generated_files[module_path] = content + ''.join(additions)
                logger.debug(f"[G-4] ✅ Added {len(additions)} symbols to {module_path}")
        
        return generated_files

    # G-5: Import vs Requirements.txt 整合性検証
    # =========================================
    
    def _validate_import_requirements_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, str]]:
        """
        Pythonファイル内のimport文とrequirements.txtの整合性を検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            不足パッケージのリスト [{import_name, package_name, recommended_version, source_file}]
        """
        import re
        
        # import名 → PyPIパッケージ名のマッピング
        IMPORT_TO_PACKAGE = {
            'flask': 'Flask',
            'flask_sqlalchemy': 'Flask-SQLAlchemy',
            'flask_cors': 'Flask-CORS',
            'flask_login': 'Flask-Login',
            'flask_migrate': 'Flask-Migrate',
            'flask_wtf': 'Flask-WTF',
            'flask_restful': 'Flask-RESTful',
            'flask_socketio': 'Flask-SocketIO',
            'sqlalchemy': 'SQLAlchemy',
            'marshmallow': 'marshmallow',
            'dotenv': 'python-dotenv',
            'werkzeug': 'Werkzeug',
            'jinja2': 'Jinja2',
            'requests': 'requests',
            'numpy': 'numpy',
            'pandas': 'pandas',
            'PIL': 'Pillow',
            'cv2': 'opencv-python',
            'sklearn': 'scikit-learn',
            'yaml': 'PyYAML',
            'jwt': 'PyJWT',
            'bcrypt': 'bcrypt',
            'passlib': 'passlib',
            'celery': 'celery',
            'redis': 'redis',
            'pymongo': 'pymongo',
            'httpx': 'httpx',
            'aiohttp': 'aiohttp',
            'pydantic': 'pydantic',
            'fastapi': 'fastapi',
            'uvicorn': 'uvicorn',
            'gunicorn': 'gunicorn',
            'pytest': 'pytest',
            'eventlet': 'eventlet',
        }
        
        # 推奨バージョン（Python 3.13対応）
        RECOMMENDED_VERSIONS = {
            'Flask': '3.0.0',
            'Flask-SQLAlchemy': '3.1.1',
            'Flask-CORS': '4.0.0',
            'Flask-Login': '0.6.3',
            'Flask-Migrate': '4.0.5',
            'Flask-WTF': '1.2.1',
            'Flask-RESTful': '0.3.10',
            'Flask-SocketIO': '5.3.6',
            'SQLAlchemy': '2.0.36',
            'marshmallow': '3.21.0',
            'python-dotenv': '1.0.0',
            'Werkzeug': '3.0.1',
            'Jinja2': '3.1.2',
            'requests': '2.31.0',
            'numpy': '1.26.4',
            'pandas': '2.2.0',
            'Pillow': '10.2.0',
            'PyYAML': '6.0.1',
            'PyJWT': '2.8.0',
            'bcrypt': '4.1.2',
            'pydantic': '2.6.0',
            'pytest': '8.0.0',
            'eventlet': '0.35.1',
        }
        
        # 標準ライブラリ（除外対象）
        STDLIB_MODULES = {
            'os', 'sys', 'json', 'datetime', 'time', 'math', 'random', 're',
            'collections', 'itertools', 'functools', 'typing', 'enum', 'uuid',
            'hashlib', 'base64', 'copy', 'io', 'pathlib', 'logging', 'traceback',
            'contextlib', 'dataclasses', 'abc', 'threading', 'multiprocessing',
            'subprocess', 'shutil', 'tempfile', 'glob', 'fnmatch', 'pickle',
            'csv', 'html', 'urllib', 'http', 'email', 'mimetypes', 'socket',
            'ssl', 'asyncio', 'concurrent', 'queue', 'struct', 'codecs',
            'locale', 'gettext', 'argparse', 'configparser', 'secrets',
            'statistics', 'decimal', 'fractions', 'numbers', 'cmath',
            'operator', 'string', 'textwrap', 'unicodedata', 'difflib',
            'heapq', 'bisect', 'array', 'weakref', 'types', 'pprint',
            'reprlib', 'warnings', 'contextlib', 'atexit', 'tracemalloc',
            'gc', 'inspect', 'dis', 'ast', 'symtable', 'token', 'keyword',
            'tokenize', 'tabnanny', 'pyclbr', 'compileall', 'zipimport',
            'importlib', 'runpy', 'builtins', 'site', 'sysconfig', 'platform',
            'errno', 'signal', 'select', 'selectors', 'mmap', 'fcntl',
            'posix', 'nt', 'posixpath', 'ntpath', 'genericpath', 'stat',
            'filecmp', 'tarfile', 'zipfile', 'gzip', 'bz2', 'lzma', 'zlib',
            'xml', 'xmlrpc', 'ipaddress', 'ftplib', 'poplib', 'imaplib',
            'nntplib', 'smtplib', 'smtpd', 'telnetlib', 'socketserver',
            'webbrowser', 'cgi', 'cgitb', 'wsgiref', 'unittest', 'doctest',
            'pdb', 'profile', 'cProfile', 'timeit', 'trace', 'faulthandler',
            'cmd', 'shlex', 'code', 'codeop', 'rlcompleter', 'tty', 'pty',
            'termios', 'curses', 'readline', 'getpass', 'netrc', 'ctypes',
            'test', '__future__', 'sqlite3', 'dbm', 'shelve', 'marshal',
        }
        
        # 1. requirements.txtから既存パッケージを抽出
        requirements_content = None
        for filepath, content in generated_files.items():
            if filepath.lower() == 'requirements.txt':
                requirements_content = content
                break
        
        if not requirements_content:
            logger.debug("[G-5] No requirements.txt found, skipping check")
            return []
        
        # 既存パッケージを小文字で正規化して保存
        existing_packages = set()
        for line in requirements_content.split('\n'):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            # package==version, package>=version, package~=version, package
            match = re.match(r'^([a-zA-Z0-9_-]+)', line)
            if match:
                existing_packages.add(match.group(1).lower().replace('-', '_'))
        
        logger.debug(f"[G-5] Existing packages in requirements.txt: {existing_packages}")
        
        # 2. Pythonファイルからimport文を抽出
        imported_modules = {}  # {import_name: source_file}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # import X / from X import Y
            import_pattern = re.compile(r'^(?:from\s+([a-zA-Z_][a-zA-Z0-9_]*)|import\s+([a-zA-Z_][a-zA-Z0-9_]*))', re.MULTILINE)
            
            for match in import_pattern.finditer(content):
                module = match.group(1) or match.group(2)
                if module:
                    # トップレベルモジュールのみ（flask_cors → flask_cors）
                    top_module = module.split('.')[0]
                    
                    # 標準ライブラリは除外
                    if top_module in STDLIB_MODULES:
                        continue
                    
                    # プロジェクト内モジュールは除外
                    is_internal = False
                    for internal_path in generated_files.keys():
                        if internal_path.endswith('.py'):
                            internal_module = internal_path.replace('/', '.').replace('\\', '.').replace('.py', '')
                            if top_module == internal_module or internal_module.startswith(top_module + '.') or top_module == internal_module.split('.')[-1]:
                                is_internal = True
                                break
                    
                    if not is_internal:
                        if top_module not in imported_modules:
                            imported_modules[top_module] = filepath
        
        logger.debug(f"[G-5] Imported external modules: {list(imported_modules.keys())}")
        
        # 3. 不足パッケージを特定
        missing_packages = []
        
        for import_name, source_file in imported_modules.items():
            # import名を正規化（flask_cors → flask_cors）
            normalized_import = import_name.lower().replace('-', '_')
            
            # 既にrequirements.txtに含まれているかチェック
            if normalized_import in existing_packages:
                continue
            
            # パッケージ名に変換（flask_cors → Flask-CORS）
            package_name = IMPORT_TO_PACKAGE.get(normalized_import, import_name)
            package_normalized = package_name.lower().replace('-', '_')
            
            # パッケージ名でも再チェック
            if package_normalized in existing_packages:
                continue
            
            # 推奨バージョンを取得
            recommended_version = RECOMMENDED_VERSIONS.get(package_name, '')
            
            missing_packages.append({
                'import_name': import_name,
                'package_name': package_name,
                'recommended_version': recommended_version,
                'source_file': source_file
            })
            
            logger.debug(f"[G-5] Missing package: {import_name} → {package_name} (from {source_file})")
        
        return missing_packages
    
    def _auto_fix_requirements_txt(
        self,
        missing_packages: List[Dict[str, str]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        不足パッケージをrequirements.txtに追加
        
        Args:
            missing_packages: 不足パッケージのリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not missing_packages:
            return generated_files
        
        # requirements.txtを探す
        requirements_path = None
        requirements_content = None
        for filepath, content in generated_files.items():
            if filepath.lower() == 'requirements.txt':
                requirements_path = filepath
                requirements_content = content
                break
        
        if not requirements_path:
            return generated_files
        
        # 追加するパッケージを構築
        additions = []
        for pkg in missing_packages:
            package_name = pkg['package_name']
            version = pkg['recommended_version']
            
            if version:
                additions.append(f"{package_name}=={version}")
            else:
                additions.append(package_name)
            
            logger.debug(f"[G-5] Adding to requirements.txt: {package_name}=={version if version else '(latest)'}")
        
        # requirements.txtに追加
        if additions:
            # 末尾に改行がなければ追加
            if not requirements_content.endswith('\n'):
                requirements_content += '\n'
            
            # パッケージを追加
            requirements_content += '\n'.join(additions) + '\n'
            
            generated_files[requirements_path] = requirements_content
            logger.debug(f"[G-5] ✅ Added {len(additions)} packages to requirements.txt")
        
        return generated_files

    # G-6: ライブラリ要求パターン検証
    # =========================================
    
    def _validate_library_requirements(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        ライブラリが要求する実装パターンを検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト [{library, requirement, file, details}]
        """
        import re
        
        issues = []
        
        # 全Pythonファイルの内容を結合して検索用に保持
        all_python_content = {}
        for filepath, content in generated_files.items():
            if filepath.endswith('.py'):
                all_python_content[filepath] = content
        
        if not all_python_content:
            return issues
        
        # ========================================
        # Check 1: Flask-Login UserMixin継承
        # ========================================
        flask_login_used = False
        user_model_file = None
        user_model_has_mixin = False
        
        for filepath, content in all_python_content.items():
            # Flask-Loginの使用を検出
            if re.search(r'from\s+flask_login\s+import|import\s+flask_login', content):
                flask_login_used = True
            if re.search(r'login_user|logout_user|current_user|login_required|LoginManager', content):
                flask_login_used = True
            
            # Userモデルを検出
            if re.search(r'class\s+User\s*\(', content):
                user_model_file = filepath
                # UserMixin継承を確認
                if re.search(r'class\s+User\s*\([^)]*UserMixin[^)]*\)', content):
                    user_model_has_mixin = True
                # UserMixinのimportも確認
                if re.search(r'from\s+flask_login\s+import[^)]*UserMixin', content):
                    user_model_has_mixin = user_model_has_mixin  # import有無だけでは判断しない
        
        if flask_login_used and user_model_file and not user_model_has_mixin:
            issues.append({
                'library': 'Flask-Login',
                'requirement': 'UserMixin inheritance',
                'file': user_model_file,
                'details': 'User model must inherit from UserMixin for Flask-Login to work',
                'fix_type': 'add_mixin',
                'pattern': {
                    'search': r'class\s+User\s*\(([^)]*)\)',
                    'mixin': 'UserMixin',
                    'import_line': 'from flask_login import UserMixin'
                }
            })
            logger.debug(f"[G-6] Flask-Login: UserMixin not inherited in {user_model_file}")
        
        # ========================================
        # Check 2: Flask-Login user_loader定義
        # ========================================
        login_manager_used = False
        user_loader_defined = False
        login_manager_file = None  # LoginManagerが定義されているファイル
        app_file = None  # app.pyまたはmain.py
        
        for filepath, content in all_python_content.items():
            if re.search(r'LoginManager\s*\(\s*\)|login_manager\s*=', content):
                login_manager_used = True
                login_manager_file = filepath  # LoginManagerが定義されているファイルを記録
                if 'main' in filepath.lower() or 'app' in filepath.lower():
                    app_file = filepath
            if re.search(r'@\s*login_manager\.user_loader|@\s*\w+\.user_loader', content):
                user_loader_defined = True
        
        if login_manager_used and not user_loader_defined:
            # 修正対象ファイルの優先順位:
            # 1. LoginManagerが定義されているファイル（user_loaderはLoginManagerと同じファイルに定義すべき）
            # 2. app.py または main.py
            # 3. フォールバック: 最初に見つかったPythonファイル
            if login_manager_file:
                target_file = login_manager_file
            elif app_file:
                target_file = app_file
            else:
                # フォールバック: generated_filesからapp.pyまたはmain.pyを探す
                for fp in all_python_content.keys():
                    if 'app.py' in fp.lower():
                        target_file = fp
                        break
                    elif 'main.py' in fp.lower():
                        target_file = fp
                        break
                else:
                    target_file = list(all_python_content.keys())[0] if all_python_content else 'app.py'
            
            issues.append({
                'library': 'Flask-Login',
                'requirement': 'user_loader callback',
                'file': target_file,
                'details': '@login_manager.user_loader callback must be defined',
                'fix_type': 'add_user_loader'
            })
            logger.debug(f"[G-6] Flask-Login: user_loader not defined")
        
        # ========================================
        # Check 3: SECRET_KEY設定 - 削除
        # ========================================
        # 理由: 「SECRET_KEYが存在するか」のチェックは「ライブラリ要求パターン」の責務外
        # SECRET_KEYの問題（クラス定義時のraise等）はG-10で検出する
        # 参照: g_checks_structural_analysis.md
        
        # ========================================
        # Check 4: Flask-WTF FlaskForm継承
        # ========================================
        flask_wtf_used = False
        form_files_without_inheritance = []
        
        for filepath, content in all_python_content.items():
            if re.search(r'from\s+flask_wtf\s+import|import\s+flask_wtf', content):
                flask_wtf_used = True
            
            # Formクラス定義を検出（FlaskFormを継承していないもの）
            form_classes = re.findall(r'class\s+(\w+Form)\s*\(([^)]*)\)', content)
            for class_name, parents in form_classes:
                if 'FlaskForm' not in parents and 'Form' in class_name:
                    form_files_without_inheritance.append({
                        'file': filepath,
                        'class': class_name,
                        'parents': parents
                    })
        
        if flask_wtf_used and form_files_without_inheritance:
            for form_info in form_files_without_inheritance:
                issues.append({
                    'library': 'Flask-WTF',
                    'requirement': 'FlaskForm inheritance',
                    'file': form_info['file'],
                    'details': f"Form class '{form_info['class']}' should inherit from FlaskForm",
                    'fix_type': 'add_form_inheritance',
                    'class_name': form_info['class']
                })
                logger.debug(f"[G-6] Flask-WTF: {form_info['class']} not inheriting FlaskForm")
        
        # ========================================
        # Check 5: SQLAlchemy back_populates双方向整合性
        # ========================================
        relationships = []  # [(file, model, field, back_populates_target)]
        
        for filepath, content in all_python_content.items():
            # relationship定義を抽出
            # relationship("TargetModel", back_populates="field_name")
            pattern = r'(\w+):\s*Mapped\[.*?\]\s*=\s*relationship\s*\(\s*["\'](\w+)["\'].*?back_populates\s*=\s*["\'](\w+)["\']'
            matches = re.findall(pattern, content, re.DOTALL)
            
            for field_name, target_model, back_populates_field in matches:
                # クラス名を取得
                class_match = re.search(r'class\s+(\w+)\s*\([^)]*\):', content[:content.find(field_name)])
                if class_match:
                    model_name = class_match.group(1)
                    relationships.append({
                        'file': filepath,
                        'model': model_name,
                        'field': field_name,
                        'target_model': target_model,
                        'back_populates': back_populates_field
                    })
        
        # 双方向チェック
        for rel in relationships:
            # 対応するback_populatesが存在するか確認
            counterpart_found = False
            for other_rel in relationships:
                if (other_rel['model'] == rel['target_model'] and 
                    other_rel['field'] == rel['back_populates'] and
                    other_rel['target_model'] == rel['model']):
                    counterpart_found = True
                    break
            
            if not counterpart_found:
                # 対象モデルのファイルを探す
                target_file = None
                for filepath, content in all_python_content.items():
                    if re.search(rf'class\s+{rel["target_model"]}\s*\(', content):
                        target_file = filepath
                        break
                
                if target_file:
                    issues.append({
                        'library': 'SQLAlchemy',
                        'requirement': 'back_populates bidirectional',
                        'file': target_file,
                        'details': f"Model '{rel['target_model']}' missing back_populates='{rel['back_populates']}' for relationship with '{rel['model']}'",
                        'fix_type': 'add_back_populates',
                        'source_model': rel['model'],
                        'target_model': rel['target_model'],
                        'field_name': rel['back_populates']
                    })
                    logger.debug(f"[G-6] SQLAlchemy: Missing back_populates in {rel['target_model']}")
        
        if issues:
            logger.debug(f"[G-6] Found {len(issues)} library requirement issues")
        else:
            logger.debug("[G-6] All library requirements satisfied")
        
        return issues
    
    def _auto_fix_library_requirements(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        ライブラリ要求パターン問題をLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # ファイルごとに問題をグループ化
        issues_by_file = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in issues_by_file:
                issues_by_file[filepath] = []
            issues_by_file[filepath].append(issue)
        
        # 各ファイルを修正
        for filepath, file_issues in issues_by_file.items():
            if filepath not in generated_files:
                logger.debug(f"[G-6] File not found: {filepath}")
                continue
            
            current_code = generated_files[filepath]
            
            # 問題の詳細を整形
            issue_descriptions = []
            for i, issue in enumerate(file_issues, 1):
                issue_descriptions.append(
                    f"{i}. [{issue['library']}] {issue['requirement']}\n"
                    f"   Details: {issue['details']}"
                )
            
            issues_text = "\n".join(issue_descriptions)
            
            # 修正プロンプト構築
            fix_prompt = f"""You are fixing library requirement issues in a Flask application.

## File: {filepath}

## Issues to Fix
{issues_text}

## Current Code
```python
{current_code}
```

## Fix Instructions
1. For UserMixin inheritance: Add 'from flask_login import UserMixin' and add UserMixin to the class inheritance
   - Example: class User(UserMixin, db.Model):
2. For user_loader: Add @login_manager.user_loader decorator with proper callback function
3. For SECRET_KEY: Add SECRET_KEY configuration in the Config class
4. For FlaskForm: Change Form inheritance to FlaskForm and add proper import
5. For back_populates: Add the missing relationship with correct back_populates reference

## Output
Return ONLY the complete fixed Python code. Do not include explanations.
Do not change anything else in the code - only fix the specific issues listed above.

```python
"""
            
            try:
                logger.debug(f"[G-6] Fixing {len(file_issues)} issues in {filepath}...")
                
                response = self._call_llm_api(fix_prompt)
                
                if response:
                    # Pythonコードを抽出
                    import re
                    code_match = re.search(r'```python\s*\n(.*?)```', response, re.DOTALL)
                    if code_match:
                        fixed_code = code_match.group(1).strip()
                    else:
                        # ```python がない場合は全体をコードとして扱う
                        fixed_code = response.strip()
                        # 先頭の```pythonと末尾の```を除去
                        if fixed_code.startswith('```'):
                            fixed_code = re.sub(r'^```\w*\n?', '', fixed_code)
                        if fixed_code.endswith('```'):
                            fixed_code = fixed_code[:-3].strip()
                    
                    if fixed_code and len(fixed_code) > 100:  # 最低限のコード長チェック
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-6] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-6] ⚠ Generated code too short for {filepath}")
                else:
                    logger.debug(f"[G-6] ⚠ No response from LLM for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-6] ⚠ Error fixing {filepath}: {e}")
        
        # G-6で修正したライブラリをrequirements.txtにも追加
        # 対象: Flask-Login, Flask-WTF, Flask-Bcrypt
        libraries_fixed = {
            issue.get('library') 
            for issue in issues 
            if issue.get('library')
        }
        
        if libraries_fixed:
            # requirements.txtを探す
            requirements_path = None
            requirements_content = None
            for fp, content in generated_files.items():
                if fp.lower() == 'requirements.txt':
                    requirements_path = fp
                    requirements_content = content
                    break
            
            if requirements_path and requirements_content:
                # 既存パッケージを抽出
                import re
                existing_packages = set()
                for line in requirements_content.split('\n'):
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
                    match = re.match(r'^([a-zA-Z0-9_-]+)', line)
                    if match:
                        existing_packages.add(match.group(1).lower().replace('-', '_'))
                
                # 修正されたライブラリに応じてrequirements.txtに追加
                libraries_to_add = []
                
                # Flask-Login
                if 'Flask-Login' in libraries_fixed and 'flask_login' not in existing_packages:
                    libraries_to_add.append('Flask-Login==0.6.3')
                
                # Flask-WTF
                if 'Flask-WTF' in libraries_fixed and 'flask_wtf' not in existing_packages:
                    libraries_to_add.append('Flask-WTF==1.2.1')
                
                # 追加実行
                if libraries_to_add:
                    if not requirements_content.endswith('\n'):
                        requirements_content += '\n'
                    requirements_content += '\n'.join(libraries_to_add) + '\n'
                    generated_files[requirements_path] = requirements_content
                    logger.debug(f"[G-6] ✅ Added {len(libraries_to_add)} packages to requirements.txt: {', '.join(libraries_to_add)}")
        
        return generated_files

    # G-7: フロントエンド依存整合性検証
    # =========================================
    
    def _validate_frontend_dependencies(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        フロントエンド依存の整合性を検証
        - JS import ↔ package.json
        - HTML script/link ↔ ファイル存在
        - CSS @import ↔ ファイル存在
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト [{type, file, dependency, details}]
        """
        import re
        
        issues = []
        
        # 生成されたファイルのパスをセットに
        generated_paths = set(generated_files.keys())
        # 小文字化したパスも用意（大文字小文字を無視した比較用）
        generated_paths_lower = {p.lower() for p in generated_paths}
        
        # ========================================
        # Check 1: HTML script/link参照 ↔ ファイル存在
        # ========================================
        for filepath, content in generated_files.items():
            if not filepath.endswith('.html'):
                continue
            
            # HTMLファイルのディレクトリを取得（相対パス解決用）
            html_dir = os.path.dirname(filepath) if os.path.dirname(filepath) else ''
            
            # <script src="..."> を検出
            script_refs = re.findall(r'<script[^>]+src=["\']([^"\']+)["\']', content)
            for src in script_refs:
                # 外部URL（http://, https://, //）はスキップ
                if src.startswith(('http://', 'https://', '//')):
                    continue
                # CDN参照はスキップ
                if 'cdn' in src.lower() or 'cloudflare' in src.lower():
                    continue
                
                # 🆕 相対パスを解決（HTMLファイルのディレクトリを基準）
                normalized_src = src.lstrip('/')
                if html_dir and not normalized_src.startswith('/'):
                    # 相対パスの場合、HTMLファイルのディレクトリから解決
                    resolved_path = os.path.normpath(os.path.join(html_dir, normalized_src))
                else:
                    resolved_path = normalized_src
                
                # static/プレフィックスの処理
                if resolved_path.startswith('static/'):
                    check_path = resolved_path
                else:
                    check_path = resolved_path  # 相対パス解決済みなのでそのまま使用
                
                # 複数のパスパターンでファイル存在チェック
                possible_paths = [
                    check_path.lower(),
                    resolved_path.lower(),
                    normalized_src.lower(),
                    f"static/{resolved_path}".lower(),
                ]
                
                if not any(p in generated_paths_lower for p in possible_paths):
                    issues.append({
                        'type': 'missing_script',
                        'file': filepath,
                        'dependency': src,
                        'expected_path': resolved_path,  # 解決後のパスを使用
                        'details': f"Script file '{src}' referenced in HTML but not found"
                    })
                    logger.debug(f"[G-7] Missing script: {src} in {filepath}")
            
            # <link href="..."> を検出（CSS）
            link_refs = re.findall(r'<link[^>]+href=["\']([^"\']+\.css)["\']', content)
            for href in link_refs:
                # 外部URLはスキップ
                if href.startswith(('http://', 'https://', '//')):
                    continue
                
                # 🆕 相対パスを解決（HTMLファイルのディレクトリを基準）
                normalized_href = href.lstrip('/')
                if html_dir and not normalized_href.startswith('/'):
                    resolved_path = os.path.normpath(os.path.join(html_dir, normalized_href))
                else:
                    resolved_path = normalized_href
                
                # static/プレフィックスの処理
                if resolved_path.startswith('static/'):
                    check_path = resolved_path
                else:
                    check_path = resolved_path
                
                # 複数のパスパターンでファイル存在チェック
                possible_paths = [
                    check_path.lower(),
                    resolved_path.lower(),
                    normalized_href.lower(),
                    f"static/{resolved_path}".lower(),
                ]
                
                if not any(p in generated_paths_lower for p in possible_paths):
                    issues.append({
                        'type': 'missing_css',
                        'file': filepath,
                        'dependency': href,
                        'expected_path': resolved_path,
                        'details': f"CSS file '{href}' referenced in HTML but not found"
                    })
                    logger.debug(f"[G-7] Missing CSS: {href} in {filepath}")
        
        # ========================================
        # Check 2: CSS @import ↔ ファイル存在
        # ========================================
        for filepath, content in generated_files.items():
            if not filepath.endswith('.css'):
                continue
            
            # CSSファイルのディレクトリを取得
            css_dir = os.path.dirname(filepath) if os.path.dirname(filepath) else ''
            
            # @import url("...") または @import "..." を検出
            import_refs = re.findall(r'@import\s+(?:url\(["\']?([^"\')\s]+)["\']?\)|["\']([^"\']+)["\'])', content)
            for match in import_refs:
                import_path = match[0] or match[1]
                if not import_path:
                    continue
                
                # 外部URLはスキップ
                if import_path.startswith(('http://', 'https://', '//')):
                    continue
                
                # 🆕 相対パスを解決（os.path.normpathを使用）
                normalized_import = import_path.lstrip('./')
                if css_dir and not import_path.startswith('/'):
                    resolved_path = os.path.normpath(os.path.join(css_dir, import_path))
                else:
                    resolved_path = normalized_import
                
                # 複数のパスパターンでファイル存在チェック
                possible_paths = [
                    resolved_path.lower(),
                    normalized_import.lower(),
                    import_path.lstrip('./').lower(),
                ]
                
                if not any(p in generated_paths_lower for p in possible_paths):
                    issues.append({
                        'type': 'missing_css_import',
                        'file': filepath,
                        'dependency': import_path,
                        'expected_path': resolved_path,
                        'details': f"CSS import '{import_path}' not found"
                    })
                    logger.debug(f"[G-7] Missing CSS import: {import_path} in {filepath}")
        
        # ========================================
        # Check 3: JS fetch API URL ↔ Flask Route
        # ========================================
        # Flask routeを収集
        flask_routes = set()
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # @app.route('/...') または @bp.route('/...')
            route_matches = re.findall(r'@\w+\.route\s*\(\s*["\']([^"\']+)["\']', content)
            for route in route_matches:
                # パラメータを正規化 (/<int:id> -> /<param>)
                normalized_route = re.sub(r'<\w+:\w+>', '<param>', route)
                normalized_route = re.sub(r'<\w+>', '<param>', normalized_route)
                flask_routes.add(normalized_route)
        
        # JSファイルのfetch URLをチェック
        for filepath, content in generated_files.items():
            if not filepath.endswith('.js'):
                continue
            
            # fetch('/api/...') を検出
            fetch_urls = re.findall(r'fetch\s*\(\s*["\']([^"\']+)["\']', content)
            # axios.get/post等も検出
            axios_urls = re.findall(r'axios\.\w+\s*\(\s*["\']([^"\']+)["\']', content)
            
            all_api_urls = fetch_urls + axios_urls
            
            for url in all_api_urls:
                # 外部URLはスキップ
                if url.startswith(('http://', 'https://', '//')):
                    continue
                
                # テンプレートリテラル（${...}）を含むURLは部分マッチで確認
                if '${' in url or '{' in url:
                    # 動的部分を正規化
                    normalized_url = re.sub(r'\$\{[^}]+\}', '<param>', url)
                    normalized_url = re.sub(r'\{[^}]+\}', '<param>', normalized_url)
                else:
                    normalized_url = url
                
                # パスパラメータを正規化
                normalized_url = re.sub(r'/\d+', '/<param>', normalized_url)
                
                # routeに存在するか確認
                route_found = False
                for route in flask_routes:
                    # 完全一致または前方一致
                    if normalized_url == route or normalized_url.rstrip('/') == route.rstrip('/'):
                        route_found = True
                        break
                    # パラメータ正規化後の比較
                    if re.sub(r'<param>', '<param>', normalized_url) == re.sub(r'<param>', '<param>', route):
                        route_found = True
                        break
                
                # routeが全くない場合（空の場合）は警告しない
                if flask_routes and not route_found and url.startswith('/'):
                    # /auth/, /api/ などの一般的なプレフィックスがあるか確認
                    url_prefix = '/' + url.split('/')[1] + '/' if len(url.split('/')) > 1 else url
                    prefix_exists = any(r.startswith(url_prefix) for r in flask_routes)
                    
                    if not prefix_exists:
                        issues.append({
                            'type': 'missing_route',
                            'file': filepath,
                            'dependency': url,
                            'details': f"API endpoint '{url}' used in JS but no matching Flask route found"
                        })
                        logger.debug(f"[G-7] Missing route for: {url} in {filepath}")
        
        if issues:
            logger.debug(f"[G-7] Found {len(issues)} frontend dependency issues")
        else:
            logger.debug("[G-7] All frontend dependencies satisfied")
        
        return issues
    
    def _auto_fix_frontend_dependencies(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        フロントエンド依存問題をLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # 問題タイプごとにグループ化
        missing_files = []  # 新規ファイル作成が必要
        missing_routes = []  # Flaskルート追加が必要
        
        for issue in issues:
            if issue['type'] in ('missing_script', 'missing_css', 'missing_css_import'):
                missing_files.append(issue)
            elif issue['type'] == 'missing_route':
                missing_routes.append(issue)
        
        # ========================================
        # 1. 不足ファイルの生成
        # ========================================
        if missing_files:
            # ファイルタイプごとにグループ化
            files_to_create = {}
            for issue in missing_files:
                expected_path = issue.get('expected_path', issue['dependency'])
                if expected_path not in files_to_create:
                    files_to_create[expected_path] = issue
            
            for filepath, issue in files_to_create.items():
                # 既に存在する場合はスキップ
                if filepath in generated_files:
                    continue
                
                # ファイルタイプに応じたプロンプト
                if filepath.endswith('.js'):
                    file_type = "JavaScript"
                    template = "// JavaScript file\n"
                elif filepath.endswith('.css'):
                    file_type = "CSS"
                    template = "/* CSS file */\n"
                else:
                    continue
                
                # 参照元ファイルのコンテキストを取得
                referrer_content = generated_files.get(issue['file'], '')
                
                prompt = f"""Generate a {file_type} file that is referenced but missing.

## Missing File: {filepath}
## Referenced from: {issue['file']}

## Context (referrer file excerpt):
```
{referrer_content[:2000]}
```

## Requirements:
1. Create a minimal but functional {file_type} file
2. Include basic structure appropriate for the application
3. For JS: Include any event listeners or API calls that might be expected
4. For CSS: Include basic styling that matches the HTML structure

## Output
Return ONLY the {file_type} code without markdown code blocks.
"""
                
                try:
                    logger.debug(f"[G-7] Generating missing file: {filepath}")
                    
                    response = self._call_llm_api(prompt)
                    
                    if response:
                        # コードブロックを除去
                        import re
                        code = response.strip()
                        code = re.sub(r'^```\w*\n?', '', code)
                        code = re.sub(r'\n?```$', '', code)
                        
                        if code and len(code) > 20:
                            generated_files[filepath] = code
                            logger.debug(f"[G-7] ✅ Created {filepath}")
                        else:
                            logger.debug(f"[G-7] ⚠ Generated content too short for {filepath}")
                    else:
                        logger.debug(f"[G-7] ⚠ No response for {filepath}")
                        
                except Exception as e:
                    logger.debug(f"[G-7] ⚠ Error creating {filepath}: {e}")
        
        # ========================================
        # 2. 不足Flaskルートの追加
        # ========================================
        if missing_routes:
            # views/api.py または適切なファイルを探す
            api_file = None
            for filepath in generated_files.keys():
                if 'api' in filepath.lower() and filepath.endswith('.py'):
                    api_file = filepath
                    break
                if 'views' in filepath.lower() and filepath.endswith('.py'):
                    api_file = filepath
            
            if api_file and api_file in generated_files:
                current_code = generated_files[api_file]
                
                # 不足ルートの詳細
                routes_to_add = []
                for issue in missing_routes:
                    routes_to_add.append(f"- {issue['dependency']}")
                
                routes_text = "\n".join(routes_to_add)
                
                prompt = f"""Add missing API routes to this Flask file.

## File: {api_file}

## Missing Routes (referenced in JavaScript but not defined):
{routes_text}

## Current Code:
```python
{current_code}
```

## Requirements:
1. Add stub routes for each missing endpoint
2. Return appropriate JSON responses
3. Use @login_required if the route seems to need authentication
4. Follow the existing code style and patterns
5. Keep all existing code unchanged

## Output
Return ONLY the complete Python code with the new routes added.
"""
                
                try:
                    logger.debug(f"[G-7] Adding {len(missing_routes)} missing routes to {api_file}")
                    
                    response = self._call_llm_api(prompt)
                    
                    if response:
                        import re
                        code_match = re.search(r'```python\s*\n(.*?)```', response, re.DOTALL)
                        if code_match:
                            fixed_code = code_match.group(1).strip()
                        else:
                            fixed_code = response.strip()
                            if fixed_code.startswith('```'):
                                fixed_code = re.sub(r'^```\w*\n?', '', fixed_code)
                            if fixed_code.endswith('```'):
                                fixed_code = fixed_code[:-3].strip()
                        
                        if fixed_code and len(fixed_code) > len(current_code) * 0.8:
                            generated_files[api_file] = fixed_code
                            logger.debug(f"[G-7] ✅ Added routes to {api_file}")
                        else:
                            logger.debug(f"[G-7] ⚠ Generated code too short for {api_file}")
                    else:
                        logger.debug(f"[G-7] ⚠ No response for route additions")
                        
                except Exception as e:
                    logger.debug(f"[G-7] ⚠ Error adding routes: {e}")
        
        return generated_files

    # G-8: Model↔Schema整合性検証
    # =========================================
    
    def _validate_model_schema_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemyモデルとMarshmallowスキーマのフィールド整合性を検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト [{type, model, schema, field, details}]
        """
        import re
        
        issues = []
        
        # ========================================
        # Step 1: モデルからカラム定義を抽出
        # ========================================
        models = {}  # {ModelName: {file, columns: [column_names]}}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # クラス定義を検出
            class_matches = re.finditer(
                r'class\s+(\w+)\s*\([^)]*(?:db\.Model|Base)[^)]*\):\s*\n(.*?)(?=\nclass\s|\Z)',
                content,
                re.DOTALL
            )
            
            for match in class_matches:
                model_name = match.group(1)
                class_body = match.group(2)
                
                columns = []
                
                # SQLAlchemy 2.0スタイル: field: Mapped[type] = mapped_column(...)
                mapped_columns = re.findall(
                    r'^\s+(\w+):\s*Mapped\[',
                    class_body,
                    re.MULTILINE
                )
                columns.extend(mapped_columns)
                
                # 旧スタイル: field = Column(...)
                old_columns = re.findall(
                    r'^\s+(\w+)\s*=\s*(?:Column|mapped_column)\s*\(',
                    class_body,
                    re.MULTILINE
                )
                columns.extend(old_columns)
                
                # relationship は除外（スキーマには通常含まれない）
                relationships = re.findall(
                    r'^\s+(\w+):\s*Mapped\[.*?\]\s*=\s*relationship\s*\(',
                    class_body,
                    re.MULTILINE
                )
                columns = [c for c in columns if c not in relationships]
                
                # 重複を除去
                columns = list(set(columns))
                
                if columns:
                    models[model_name] = {
                        'file': filepath,
                        'columns': columns
                    }
        
        logger.debug(f"[G-8] Found {len(models)} models: {list(models.keys())}")
        
        # ========================================
        # Step 2: スキーマからフィールド定義を抽出
        # ========================================
        schemas = {}  # {SchemaName: {file, fields: [field_names], model_name}}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            if 'schema' not in filepath.lower():
                continue
            
            # Schemaクラス定義を検出
            schema_matches = re.finditer(
                r'class\s+(\w+Schema)\s*\([^)]*Schema[^)]*\):\s*\n(.*?)(?=\nclass\s|\Z)',
                content,
                re.DOTALL
            )
            
            for match in schema_matches:
                schema_name = match.group(1)
                class_body = match.group(2)
                
                fields = []
                
                # Marshmallowフィールド: field = fields.Type(...) を抽出
                # フィールド名と定義全体を取得
                field_pattern = r'^\s+(\w+)\s*=\s*(fields\.[^\n]+)'
                field_matches = re.findall(field_pattern, class_body, re.MULTILINE)
                
                # Nested, Method, Function, Pluck等は除外（モデルカラムと対応しない）
                # これらはrelationship経由や計算フィールドであり、モデルに直接対応しない
                EXCLUDE_FIELD_PATTERNS = ['Nested', 'Method', 'Function', 'Pluck', 'List(fields.Nested']
                for field_name, field_def in field_matches:
                    should_exclude = False
                    for exclude_pattern in EXCLUDE_FIELD_PATTERNS:
                        if exclude_pattern in field_def:
                            should_exclude = True
                            logger.debug(f"[G-8] Excluding field '{field_name}' (type contains '{exclude_pattern}')")
                            break
                    if not should_exclude:
                        fields.append(field_name)
                
                # dump_only, load_only 等のメタ情報も含む
                # class Meta: model = ModelName を検出
                model_match = re.search(
                    r'class\s+Meta\s*:.*?model\s*=\s*(\w+)',
                    class_body,
                    re.DOTALL
                )
                
                # スキーマ名からモデル名を推測 (UserSchema -> User)
                inferred_model = schema_name.replace('Schema', '').replace('Create', '').replace('Update', '').replace('Response', '')
                model_name = model_match.group(1) if model_match else inferred_model
                
                if fields:
                    schemas[schema_name] = {
                        'file': filepath,
                        'fields': fields,
                        'model_name': model_name
                    }
        
        logger.debug(f"[G-8] Found {len(schemas)} schemas: {list(schemas.keys())}")
        
        # ========================================
        # Step 3: 整合性チェック
        # ========================================
        for schema_name, schema_info in schemas.items():
            model_name = schema_info['model_name']
            
            if model_name not in models:
                # 対応するモデルが見つからない（警告のみ）
                logger.debug(f"[G-8] Schema '{schema_name}' references model '{model_name}' which was not found")
                continue
            
            model_columns = set(models[model_name]['columns'])
            schema_fields = set(schema_info['fields'])
            
            # スキーマにあるがモデルにないフィールド
            extra_in_schema = schema_fields - model_columns
            # 一般的な追加フィールド（id, created_at等）は除外
            common_fields = {'id', 'created_at', 'updated_at', 'password', 'password_hash'}
            extra_in_schema = extra_in_schema - common_fields
            
            for field in extra_in_schema:
                # フィールド名が類似しているか確認（typoの可能性）
                similar_columns = [c for c in model_columns if field.lower() in c.lower() or c.lower() in field.lower()]
                
                issues.append({
                    'type': 'schema_field_not_in_model',
                    'schema': schema_name,
                    'schema_file': schema_info['file'],
                    'model': model_name,
                    'model_file': models[model_name]['file'],
                    'field': field,
                    'similar_columns': similar_columns,
                    'details': f"Schema field '{field}' not found in model '{model_name}'"
                })
                logger.debug(f"[G-8] Schema '{schema_name}' has field '{field}' not in model '{model_name}'")
        
        if issues:
            logger.debug(f"[G-8] Found {len(issues)} model-schema consistency issues")
        else:
            logger.debug("[G-8] All model-schema consistencies satisfied")
        
        return issues
    
    def _auto_fix_model_schema_consistency(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Model↔Schema整合性問題をLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # スキーマファイルごとにグループ化
        issues_by_schema_file = {}
        for issue in issues:
            schema_file = issue['schema_file']
            if schema_file not in issues_by_schema_file:
                issues_by_schema_file[schema_file] = []
            issues_by_schema_file[schema_file].append(issue)
        
        for schema_file, file_issues in issues_by_schema_file.items():
            if schema_file not in generated_files:
                continue
            
            schema_code = generated_files[schema_file]
            
            # 対応するモデルファイルの内容を取得
            model_files_content = {}
            for issue in file_issues:
                model_file = issue['model_file']
                if model_file in generated_files and model_file not in model_files_content:
                    model_files_content[model_file] = generated_files[model_file]
            
            model_context = "\n\n".join([
                f"# {filepath}\n```python\n{content[:3000]}\n```"
                for filepath, content in model_files_content.items()
            ])
            
            # 問題の詳細
            issue_descriptions = []
            for issue in file_issues:
                desc = f"- Field '{issue['field']}' in schema '{issue['schema']}' not found in model '{issue['model']}'"
                if issue['similar_columns']:
                    desc += f" (similar columns: {issue['similar_columns']})"
                issue_descriptions.append(desc)
            
            issues_text = "\n".join(issue_descriptions)
            
            fix_prompt = f"""Fix the Marshmallow schema to match the SQLAlchemy model.

## Schema File: {schema_file}
```python
{schema_code}
```

## Related Model Files:
{model_context}

## Issues Found:
{issues_text}

## Fix Instructions:
1. If the schema field is a typo, correct it to match the model column name
2. If the schema field should not exist (not in model), remove it
3. If the schema field is intentionally different (computed, virtual), keep it but ensure it has dump_only=True or load_only=True as appropriate
4. Do NOT modify the model files - only fix the schema file
5. Keep all existing correct fields unchanged

## Output
Return ONLY the complete fixed schema Python code without markdown code blocks.
"""
            
            try:
                logger.debug(f"[G-8] Fixing schema: {schema_file}")
                
                response = self._call_llm_api(fix_prompt)
                
                if response:
                    import re
                    code = response.strip()
                    code = re.sub(r'^```\w*\n?', '', code)
                    code = re.sub(r'\n?```$', '', code)
                    
                    if code and len(code) > 100:
                        generated_files[schema_file] = code
                        logger.debug(f"[G-8] ✅ Fixed {schema_file}")
                    else:
                        logger.debug(f"[G-8] ⚠ Generated code too short for {schema_file}")
                else:
                    logger.debug(f"[G-8] ⚠ No response for {schema_file}")
                    
            except Exception as e:
                logger.debug(f"[G-8] ⚠ Error fixing {schema_file}: {e}")
        
        return generated_files

    # G-9: ドキュメント環境依存検証
    # =========================================
    
    def _validate_document_environment_commands(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        READMEやドキュメント内のコマンドが環境依存していないか検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト [{type, file, line, command, details, fix}]
        """
        import re
        
        issues = []
        
        # 環境依存コマンドのパターン
        ENV_DEPENDENT_PATTERNS = [
            # Unix専用コマンド（Windowsで動かない）
            {
                'pattern': r'^\s*(?:source|chmod|chown|ln\s+-s|tar\s+-[xczf]|grep|sed|awk|curl\s+-[^h])',
                'type': 'unix_only',
                'details': 'Unix-only command, may not work on Windows'
            },
            # Windowsで異なる環境変数設定
            {
                'pattern': r'^\s*export\s+\w+=',
                'type': 'unix_export',
                'details': 'Unix export command. Windows uses "set" or "$env:"',
                'fix': 'Add Windows alternative: set VAR=value (cmd) or $env:VAR="value" (PowerShell)'
            },
            # パス区切り文字（バックスラッシュをハードコード）
            {
                'pattern': r'[A-Za-z]:\\\\[A-Za-z]',
                'type': 'windows_path',
                'details': 'Windows-specific path. Unix uses forward slashes',
                'fix': 'Use forward slashes or provide both examples'
            },
            # cp コマンド（Windowsはcopy）
            {
                'pattern': r'^\s*cp\s+(?!-)',
                'type': 'unix_cp',
                'details': 'Unix cp command. Windows uses "copy"',
                'fix': 'Add Windows alternative or use cross-platform method'
            },
            # rm コマンド（WindowsはRemove-Itemまたはdel）
            {
                'pattern': r'^\s*rm\s+',
                'type': 'unix_rm',
                'details': 'Unix rm command. Windows uses "del" or "Remove-Item"'
            },
            # mkdir -p（Windowsは mkdir のみで可）
            {
                'pattern': r'^\s*mkdir\s+-p\s+',
                'type': 'unix_mkdir_p',
                'details': 'Unix mkdir -p. Windows mkdir creates parent dirs by default'
            },
            # venv activate が環境未指定
            {
                'pattern': r'^\s*(?:source\s+)?venv[/\\]?(?:bin|Scripts)[/\\]?activate(?!\.(ps1|bat))',
                'type': 'venv_activate_ambiguous',
                'details': 'venv activate command without specifying environment',
                'fix': 'Specify: .\\venv\\Scripts\\Activate.ps1 (PowerShell), venv\\Scripts\\activate.bat (cmd), source venv/bin/activate (Unix)'
            },
        ]
        
        # ドキュメントファイルを検索
        doc_files = []
        for filepath in generated_files.keys():
            lower_path = filepath.lower()
            if any(doc in lower_path for doc in ['readme', 'install', 'setup', 'getting_started', 'quickstart', 'contributing']):
                doc_files.append(filepath)
            if lower_path.endswith(('.md', '.rst', '.txt')) and 'doc' in lower_path:
                doc_files.append(filepath)
        
        for filepath in doc_files:
            content = generated_files[filepath]
            lines = content.split('\n')
            
            # コードブロック内のコマンドを検出
            in_code_block = False
            code_block_lang = ''
            
            for line_num, line in enumerate(lines, 1):
                # コードブロックの開始/終了を検出
                if line.strip().startswith('```'):
                    if in_code_block:
                        in_code_block = False
                        code_block_lang = ''
                    else:
                        in_code_block = True
                        # 言語指定を取得
                        code_block_lang = line.strip()[3:].lower()
                    continue
                
                # コードブロック外はスキップ
                if not in_code_block:
                    continue
                
                # bashやshellのコードブロック内のみチェック
                if code_block_lang and code_block_lang not in ('bash', 'sh', 'shell', 'console', 'powershell', 'cmd', ''):
                    continue
                
                # 各パターンをチェック
                for pattern_info in ENV_DEPENDENT_PATTERNS:
                    if re.search(pattern_info['pattern'], line):
                        issues.append({
                            'type': pattern_info['type'],
                            'file': filepath,
                            'line': line_num,
                            'command': line.strip(),
                            'details': pattern_info['details'],
                            'fix': pattern_info.get('fix', 'Provide cross-platform alternatives')
                        })
                        logger.debug(f"[G-9] {filepath}:{line_num} - {pattern_info['type']}: {line.strip()[:50]}")
        
        if issues:
            logger.debug(f"[G-9] Found {len(issues)} environment-dependent commands")
        else:
            logger.debug("[G-9] All document commands are cross-platform compatible")
        
        return issues
    
    def _auto_fix_document_environment_commands(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        ドキュメント内の環境依存コマンドをLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # ファイルごとにグループ化
        issues_by_file = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in issues_by_file:
                issues_by_file[filepath] = []
            issues_by_file[filepath].append(issue)
        
        for filepath, file_issues in issues_by_file.items():
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            
            # 問題の詳細
            issue_descriptions = []
            for issue in file_issues:
                issue_descriptions.append(
                    f"- Line {issue['line']}: `{issue['command'][:60]}...`\n"
                    f"  Problem: {issue['details']}\n"
                    f"  Fix: {issue['fix']}"
                )
            
            issues_text = "\n".join(issue_descriptions)
            
            fix_prompt = f"""Fix the environment-dependent commands in this documentation to be cross-platform compatible.

## File: {filepath}
```markdown
{content}
```

## Issues Found:
{issues_text}

## Fix Instructions:
1. For each command, provide alternatives for different operating systems
2. Use this format for shell commands:
   ```bash
   # Windows (PowerShell)
   .\\venv\\Scripts\\Activate.ps1
   
   # Windows (Command Prompt)
   venv\\Scripts\\activate.bat
   
   # Linux/Mac
   source venv/bin/activate
   ```
3. For simple commands like `cp`, show both Unix and Windows versions
4. Keep the document structure and all other content unchanged
5. Only modify the specific commands identified as issues

## Output
Return the complete fixed markdown document.
"""
            
            try:
                logger.debug(f"[G-9] Fixing document: {filepath}")
                
                response = self._call_llm_api(fix_prompt)
                
                if response:
                    import re
                    # マークダウンコードブロックを除去（レスポンス全体が```で囲まれている場合）
                    fixed_content = response.strip()
                    if fixed_content.startswith('```markdown'):
                        fixed_content = fixed_content[11:]
                    elif fixed_content.startswith('```md'):
                        fixed_content = fixed_content[5:]
                    elif fixed_content.startswith('```'):
                        fixed_content = fixed_content[3:]
                    
                    if fixed_content.endswith('```'):
                        fixed_content = fixed_content[:-3]
                    
                    fixed_content = fixed_content.strip()
                    
                    if fixed_content and len(fixed_content) > len(content) * 0.5:
                        generated_files[filepath] = fixed_content
                        logger.debug(f"[G-9] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-9] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-9] ⚠ No response for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-9] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # G-10: クラス定義安全性検証
    # =========================================
    
    def _validate_class_definition_safety(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        クラス定義本体での危険なコードを検出
        
        検出対象:
        - クラス定義本体でのraise文（import時に実行される）
        - クラス定義本体での条件付きraise（if not X: raise）
        - モジュールレベルでの危険な副作用コード
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト [{type, file, line, code, details, fix}]
        """
        import re
        import ast
        
        issues = []
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # ========================================
            # Check 1: AST解析によるクラス定義本体のraise検出
            # ========================================
            try:
                tree = ast.parse(content)
                
                for node in ast.walk(tree):
                    if isinstance(node, ast.ClassDef):
                        class_name = node.name
                        
                        # クラス定義の直接の子要素をチェック
                        for item in node.body:
                            # If文の中でraise
                            if isinstance(item, ast.If):
                                for sub_item in ast.walk(item):
                                    if isinstance(sub_item, ast.Raise):
                                        # 行番号を取得
                                        line_no = sub_item.lineno
                                        # 該当行のコードを取得
                                        lines = content.split('\n')
                                        code_line = lines[line_no - 1] if line_no <= len(lines) else ''
                                        
                                        issues.append({
                                            'type': 'class_body_conditional_raise',
                                            'file': filepath,
                                            'line': line_no,
                                            'class': class_name,
                                            'code': code_line.strip(),
                                            'details': f"Conditional raise in class '{class_name}' body executes at import time",
                                            'fix': 'Move validation to __init__ or use @property with lazy evaluation'
                                        })
                                        logger.debug(f"[G-10] {filepath}:{line_no} - Conditional raise in class '{class_name}'")
                            
                            # 直接のraise文
                            elif isinstance(item, ast.Raise):
                                line_no = item.lineno
                                lines = content.split('\n')
                                code_line = lines[line_no - 1] if line_no <= len(lines) else ''
                                
                                issues.append({
                                    'type': 'class_body_raise',
                                    'file': filepath,
                                    'line': line_no,
                                    'class': class_name,
                                    'code': code_line.strip(),
                                    'details': f"Raise statement in class '{class_name}' body executes at import time",
                                    'fix': 'Move to __init__ or use a classmethod/staticmethod'
                                })
                                logger.debug(f"[G-10] {filepath}:{line_no} - Raise in class '{class_name}' body")
                            
                            # クラス本体でのassert文（本番環境で問題になる可能性）
                            elif isinstance(item, ast.Assert):
                                line_no = item.lineno
                                lines = content.split('\n')
                                code_line = lines[line_no - 1] if line_no <= len(lines) else ''
                                
                                # Configクラスの場合のみ警告
                                if 'Config' in class_name:
                                    issues.append({
                                        'type': 'class_body_assert',
                                        'file': filepath,
                                        'line': line_no,
                                        'class': class_name,
                                        'code': code_line.strip(),
                                        'details': f"Assert in config class '{class_name}' may cause issues with python -O",
                                        'fix': 'Use explicit if/raise instead of assert for config validation'
                                    })
                                    logger.debug(f"[G-10] {filepath}:{line_no} - Assert in config class '{class_name}'")
                
            except SyntaxError as e:
                # 構文エラーは別のチェックで検出されるのでスキップ
                logger.debug(f"[G-10] Syntax error in {filepath}: {e}")
                continue
            except Exception as e:
                logger.debug(f"[G-10] Error parsing {filepath}: {e}")
                continue
            
            # ========================================
            # Check 2: 正規表現による追加パターン検出
            # ========================================
            # os.environ.get() の結果に対する即時raiseパターン
            # 例: SECRET_KEY = os.environ.get('KEY') or raise_error()
            pattern = r'=\s*os\.environ\.get\([^)]+\)\s*\n\s*if\s+not\s+\w+:\s*\n\s*raise'
            matches = list(re.finditer(pattern, content, re.MULTILINE))
            
            for match in matches:
                # 行番号を計算
                line_no = content[:match.start()].count('\n') + 1
                
                # 既にAST解析で検出されていないか確認
                already_detected = any(
                    issue['file'] == filepath and 
                    abs(issue['line'] - line_no) <= 2 
                    for issue in issues
                )
                
                if not already_detected:
                    lines = content.split('\n')
                    code_snippet = '\n'.join(lines[max(0, line_no-1):min(len(lines), line_no+2)])
                    
                    issues.append({
                        'type': 'env_var_immediate_raise',
                        'file': filepath,
                        'line': line_no,
                        'code': code_snippet[:100],
                        'details': 'Environment variable check with immediate raise at module/class level',
                        'fix': 'Add default value or move validation to runtime'
                    })
                    logger.debug(f"[G-10] {filepath}:{line_no} - Env var immediate raise pattern")
        
        if issues:
            logger.debug(f"[G-10] Found {len(issues)} class definition safety issues")
        else:
            logger.debug("[G-10] All class definitions are safe")
        
        return issues
    
    def _auto_fix_class_definition_safety(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        クラス定義安全性問題をLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # ファイルごとにグループ化
        issues_by_file = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in issues_by_file:
                issues_by_file[filepath] = []
            issues_by_file[filepath].append(issue)
        
        for filepath, file_issues in issues_by_file.items():
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            
            # 問題の詳細
            issue_descriptions = []
            for issue in file_issues:
                issue_descriptions.append(
                    f"- Line {issue['line']}: {issue['type']}\n"
                    f"  Code: `{issue['code'][:60]}...`\n"
                    f"  Problem: {issue['details']}\n"
                    f"  Fix: {issue['fix']}"
                )
            
            issues_text = "\n".join(issue_descriptions)
            
            fix_prompt = f"""Fix the class definition safety issues in this Python file.

## File: {filepath}
```python
{content}
```

## Issues Found:
{issues_text}

## Fix Instructions:
1. For class-level raise statements that check environment variables:
   - Add a default value: `SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production'`
   - Or use a property with lazy evaluation
   - Do NOT use `if not X: raise` pattern at class level

2. For config validation that must fail in production:
   - Move the validation to a separate `validate()` classmethod
   - Call this method explicitly after configuration loading, not at import time

3. Keep all other code unchanged

## Example Fix:
Before:
```python
class ProductionConfig(Config):
    SECRET_KEY = os.environ.get('SECRET_KEY')
    if not SECRET_KEY:
        raise ValueError("SECRET_KEY must be set")
```

After:
```python
class ProductionConfig(Config):
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'set-secret-key-in-production'
    
    @classmethod
    def validate(cls):
        if not os.environ.get('SECRET_KEY'):
            raise ValueError("SECRET_KEY must be set in production environment")
```

## Output
Return ONLY the complete fixed Python code without markdown code blocks.
"""
            
            try:
                logger.debug(f"[G-10] Fixing: {filepath}")
                
                response = self._call_llm_api(fix_prompt)
                
                if response:
                    import re
                    fixed_content = response.strip()
                    # マークダウンコードブロックを除去
                    fixed_content = re.sub(r'^```\w*\n?', '', fixed_content)
                    fixed_content = re.sub(r'\n?```$', '', fixed_content)
                    fixed_content = fixed_content.strip()
                    
                    if fixed_content and len(fixed_content) > len(content) * 0.5:
                        generated_files[filepath] = fixed_content
                        logger.debug(f"[G-10] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-10] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-10] ⚠ No response for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-10] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # API契約保護: 共通ユーティリティ
    # ============================================
    
    def _extract_frontend_api_field_references(
        self,
        generated_files: Dict[str, str]
    ) -> Dict[str, set]:
        """
        フロントエンドコードからAPI参照フィールドを抽出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            APIエンドポイントごとの参照フィールド {endpoint: {field1, field2, ...}}
        """
        import re
        
        api_references = {}  # {endpoint: {fields}}
        response_field_references = set()  # 全体のフィールド参照
        
        for filepath, content in generated_files.items():
            if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                continue
            
            # CRLF対応
            content = content.replace('\r\n', '\n')
            
            # パターン1: response.field, result.field, data.field
            # 例: response.id, result.user_id, data.username
            response_patterns = [
                r'(?:response|result|data|user|item|save|player)\.(\w+)',
                r'(?:response|result|data|user|item|save|player)\[[\'\"](\w+)[\'\"]\]',
            ]
            
            for pattern in response_patterns:
                for match in re.finditer(pattern, content):
                    field = match.group(1)
                    response_field_references.add(field)
            
            # パターン2: fetch呼び出しとその後のフィールドアクセス
            # 例: fetch('/api/user').then(r => r.json()).then(data => data.id)
            fetch_pattern = r'fetch\s*\(\s*[\'\"](\/[^\'\"]+)[\'\"]\s*\)'
            for match in re.finditer(fetch_pattern, content):
                endpoint = match.group(1)
                if endpoint not in api_references:
                    api_references[endpoint] = set()
            
            # パターン3: destructuring
            # 例: const { id, username } = response;
            destructure_pattern = r'const\s*\{\s*([^}]+)\s*\}\s*=\s*(?:response|result|data|await)'
            for match in re.finditer(destructure_pattern, content):
                fields_str = match.group(1)
                fields = [f.strip().split(':')[0].strip() for f in fields_str.split(',')]
                response_field_references.update(fields)
        
        # 全体の参照を 'global' キーに格納
        api_references['_global'] = response_field_references
        
        logger.debug(f"[API Contract] Extracted {len(response_field_references)} frontend field references")
        
        return api_references
    
    def _is_field_referenced_in_frontend(
        self,
        field_name: str,
        generated_files: Dict[str, str],
        api_references: Dict[str, set] = None
    ) -> bool:
        """
        フィールドがフロントエンドで参照されているかチェック
        
        Args:
            field_name: チェックするフィールド名
            generated_files: 生成されたファイル
            api_references: 事前抽出済みのAPI参照（省略時は抽出）
        
        Returns:
            フロントエンドで参照されている場合True
        """
        if api_references is None:
            api_references = self._extract_frontend_api_field_references(generated_files)
        
        global_refs = api_references.get('_global', set())
        return field_name in global_refs

    # ============================================
    # G-11: フレームワーク予約語チェック
    # ============================================
    
    def _validate_framework_reserved_words(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        フレームワーク（SQLAlchemy/Flask/Marshmallow）の予約語使用を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # SQLAlchemy Declarative API 予約語
        SQLALCHEMY_RESERVED = {
            'metadata': 'item_metadata',
            'registry': 'model_registry', 
            'query': 'db_query',
            'query_class': 'custom_query_class',
        }
        
        # Marshmallow 予約語
        MARSHMALLOW_RESERVED = {
            'fields': 'schema_fields',
            'dump': 'dump_data',
            'load': 'load_data',
            'validate': 'validate_data',
        }
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応（Windows改行コード）
            content = content.replace('\r\n', '\n')
            
            # SQLAlchemy モデルファイルの検出
            is_sqlalchemy_model = bool(re.search(
                r'class\s+\w+\s*\(\s*(db\.Model|Base)\s*\)',
                content
            ))
            
            # Marshmallow スキーマファイルの検出
            is_marshmallow_schema = bool(re.search(
                r'class\s+\w+\s*\(\s*(ma\.Schema|Schema|SQLAlchemySchema|SQLAlchemyAutoSchema)\s*\)',
                content
            ))
            
            if is_sqlalchemy_model:
                # SQLAlchemy予約語チェック
                for reserved_word, suggested_name in SQLALCHEMY_RESERVED.items():
                    # カラム定義パターン: reserved_word: Mapped[...] = または reserved_word = Column(...)
                    # [ \t]+ を使用（\s+は改行を含むため使用しない）
                    pattern = rf'^[ \t]+{reserved_word}[ \t]*[:=]'
                    matches = re.finditer(pattern, content, re.MULTILINE)
                    for match in matches:
                        line_no = content[:match.start()].count('\n') + 1
                        line_content = content.split('\n')[line_no - 1].strip()
                        
                        issues.append({
                            'type': 'sqlalchemy_reserved_word',
                            'file': filepath,
                            'line': line_no,
                            'reserved_word': reserved_word,
                            'suggested_name': suggested_name,
                            'code': line_content,
                            'details': f"'{reserved_word}' is reserved in SQLAlchemy Declarative API"
                        })
                        logger.debug(f"[G-11] {filepath}:{line_no} - SQLAlchemy reserved word '{reserved_word}'")
            
            if is_marshmallow_schema:
                # Marshmallow予約語チェック（フィールド名として使用している場合）
                for reserved_word, suggested_name in MARSHMALLOW_RESERVED.items():
                    # フィールド定義パターン: reserved_word = fields.XXX(...)
                    # [ \t]+ を使用（\s+は改行を含むため使用しない）
                    pattern = rf'^[ \t]+{reserved_word}[ \t]*=[ \t]*fields\.'
                    matches = re.finditer(pattern, content, re.MULTILINE)
                    for match in matches:
                        line_no = content[:match.start()].count('\n') + 1
                        line_content = content.split('\n')[line_no - 1].strip()
                        
                        issues.append({
                            'type': 'marshmallow_reserved_word',
                            'file': filepath,
                            'line': line_no,
                            'reserved_word': reserved_word,
                            'suggested_name': suggested_name,
                            'code': line_content,
                            'details': f"'{reserved_word}' conflicts with Marshmallow Schema attribute"
                        })
                        logger.debug(f"[G-11] {filepath}:{line_no} - Marshmallow reserved word '{reserved_word}'")
        
        if issues:
            logger.debug(f"[G-11] Found {len(issues)} framework reserved word issues")
        else:
            logger.debug("[G-11] No framework reserved word issues found")
        
        return issues
    
    def _auto_fix_framework_reserved_words(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        フレームワーク予約語の問題をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        # API契約保護: フロントエンドで参照されているフィールドは変更しない
        api_references = self._extract_frontend_api_field_references(generated_files)
        protected_issues = []
        safe_issues = []
        
        for issue in issues:
            field_name = issue.get('reserved_word', '')
            if self._is_field_referenced_in_frontend(field_name, generated_files, api_references):
                protected_issues.append(issue)
                logger.debug(f"[G-11] ⚠ Skipping '{field_name}' - referenced in frontend (API contract protection)")
            else:
                safe_issues.append(issue)
        
        if protected_issues:
            logger.debug(f"[G-11] Protected {len(protected_issues)} fields due to frontend references")
        
        # 安全に変更できるissuesのみ処理
        files_to_fix = {}
        for issue in safe_issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            # 修正情報を構築
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: Rename '{issue['reserved_word']}' to '{issue['suggested_name']}'"
                )
            
            # 他のファイルで参照している可能性があるファイルを収集
            related_files = {}
            reserved_words = [i['reserved_word'] for i in file_issues]
            for other_path, other_content in generated_files.items():
                if other_path != filepath:
                    for rw in reserved_words:
                        if rw in other_content:
                            related_files[other_path] = other_content
                            break
            
            prompt = f"""Fix the following framework reserved word issues in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Related files that may need reference updates:**
"""
            for rpath, rcontent in list(related_files.items())[:3]:
                prompt += f"\n## {rpath}\n```python\n{rcontent[:2000]}\n```\n"
            
            prompt += """

**Instructions:**
1. Rename ONLY the reserved word column/field names to the suggested names
2. Update ALL references to these renamed attributes within this file
3. Do NOT change any other code or logic
4. Return the complete fixed file

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-11] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a Python code refactoring expert. Rename reserved words while preserving all functionality.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                # コード抽出
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-11] ✅ Fixed {filepath}")
                        
                        # 関連ファイルの参照も更新
                        for issue in file_issues:
                            old_name = issue['reserved_word']
                            new_name = issue['suggested_name']
                            for other_path in related_files:
                                if other_path in generated_files:
                                    other_content = generated_files[other_path]
                                    
                                    # 属性アクセスパターン: .metadata → .item_metadata
                                    other_content = re.sub(
                                        rf'\.{old_name}\b',
                                        f'.{new_name}',
                                        other_content
                                    )
                                    
                                    # Marshmallowスキーマのフィールド定義パターン
                                    # metadata = fields.XXX → item_metadata = fields.XXX
                                    other_content = re.sub(
                                        rf'^([ \t]+){old_name}(\s*=\s*fields\.)',
                                        rf'\1{new_name}\2',
                                        other_content,
                                        flags=re.MULTILINE
                                    )
                                    
                                    # SQLAlchemyAutoSchemaの場合、model_fieldsのマッピングも更新
                                    # 'metadata': ... → 'item_metadata': ...
                                    other_content = re.sub(
                                        rf"(['\"]){old_name}(['\"])\s*:",
                                        rf"\1{new_name}\2:",
                                        other_content
                                    )
                                    
                                    generated_files[other_path] = other_content
                                    logger.debug(f"[G-11] Updated references in {other_path}")
                    else:
                        logger.debug(f"[G-11] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-11] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-11] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-12: Python組み込み名シャドウイングチェック
    # ============================================
    
    def _validate_python_builtin_shadowing(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Python組み込み名のシャドウイング（上書き）を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        import builtins
        
        issues = []
        
        # 危険度の高い組み込み名（SQLAlchemyモデルでよく誤用される）
        HIGH_RISK_BUILTINS = {
            'id': 'item_id',
            'type': 'item_type',
            'list': 'items_list',
            'dict': 'data_dict',
            'str': 'text_str',
            'int': 'number_int',
            'bool': 'flag_bool',
            'set': 'items_set',
            'hash': 'hash_value',
            'input': 'user_input',
            'filter': 'data_filter',
            'format': 'text_format',
            'map': 'data_map',
            'object': 'base_object',
            'property': 'item_property',
            'range': 'value_range',
            'slice': 'data_slice',
            'sum': 'total_sum',
            'max': 'max_value',
            'min': 'min_value',
            'open': 'file_open',
            'file': 'file_obj',
            'dir': 'directory',
            'vars': 'variables',
            'globals': 'global_vars',
            'locals': 'local_vars',
        }
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応（Windows改行コード）
            content = content.replace('\r\n', '\n')
            
            # クラス定義内の属性をチェック
            class_pattern = r'class\s+(\w+)\s*\(([^)]*)\)\s*:'
            for class_match in re.finditer(class_pattern, content):
                class_name = class_match.group(1)
                class_parents = class_match.group(2)
                class_start = class_match.end()
                
                # Marshmallowスキーマはスキップ（APIフィールド名なので変更不可）
                if 'Schema' in class_parents:
                    continue
                
                # クラス本体を抽出（次のクラスまたはファイル末尾まで）
                next_class = re.search(r'\nclass\s+\w+', content[class_start:])
                if next_class:
                    class_body = content[class_start:class_start + next_class.start()]
                else:
                    class_body = content[class_start:]
                
                # 属性定義をチェック
                for builtin_name, suggested_name in HIGH_RISK_BUILTINS.items():
                    # パターン: builtin_name: Mapped[...] = または builtin_name = Column(...)
                    # [ \t]+ を使用（\s+は改行を含むため使用しない）
                    attr_pattern = rf'^[ \t]+{builtin_name}[ \t]*[:=]'
                    for match in re.finditer(attr_pattern, class_body, re.MULTILINE):
                        # 絶対位置を計算
                        abs_pos = class_start + match.start()
                        line_no = content[:abs_pos].count('\n') + 1
                        line_content = content.split('\n')[line_no - 1].strip()
                        
                        # 'id'は主キーとしてよく使われるため、特別扱い
                        if builtin_name == 'id' and 'primary_key' in line_content:
                            continue  # 主キーとしてのidは許容
                        
                        # Marshmallowフィールド定義は除外（fields.Int, fields.Str等）
                        if 'fields.' in line_content:
                            continue
                        
                        issues.append({
                            'type': 'python_builtin_shadowing',
                            'file': filepath,
                            'line': line_no,
                            'class': class_name,
                            'builtin_name': builtin_name,
                            'suggested_name': suggested_name,
                            'code': line_content,
                            'details': f"'{builtin_name}' shadows Python builtin in class '{class_name}'"
                        })
                        logger.debug(f"[G-12] {filepath}:{line_no} - Builtin shadowing '{builtin_name}' in {class_name}")
        
        if issues:
            logger.debug(f"[G-12] Found {len(issues)} Python builtin shadowing issues")
        else:
            logger.debug("[G-12] No Python builtin shadowing issues found")
        
        return issues
    
    def _auto_fix_python_builtin_shadowing(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Python組み込み名シャドウイングをLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        # API契約保護: フロントエンドで参照されているフィールドは変更しない
        api_references = self._extract_frontend_api_field_references(generated_files)
        protected_issues = []
        safe_issues = []
        
        for issue in issues:
            field_name = issue.get('builtin_name', '')
            if self._is_field_referenced_in_frontend(field_name, generated_files, api_references):
                protected_issues.append(issue)
                logger.debug(f"[G-12] ⚠ Skipping '{field_name}' - referenced in frontend (API contract protection)")
            else:
                safe_issues.append(issue)
        
        if protected_issues:
            logger.debug(f"[G-12] Protected {len(protected_issues)} fields due to frontend references")
        
        # 安全に変更できるissuesのみ処理
        files_to_fix = {}
        for issue in safe_issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: Rename '{issue['builtin_name']}' to '{issue['suggested_name']}' in class {issue['class']}"
                )
            
            prompt = f"""Fix Python builtin name shadowing issues in this file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Rename the builtin-shadowing attribute names to the suggested names
2. Update ALL references to these renamed attributes within this file
3. Preserve primary key 'id' fields - do NOT rename those
4. Do NOT change any other code or logic

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-12] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a Python code refactoring expert. Rename builtin-shadowing names while preserving functionality.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-12] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-12] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-12] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-12] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-13: Flask Blueprintルート重複チェック
    # ============================================
    
    def _validate_flask_route_duplicates(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Flask Blueprint間のルート重複を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # Step 1: main.py/app.pyからregister_blueprintのurl_prefixを収集
        blueprint_prefixes = {}  # blueprint変数名 -> url_prefix
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応
            content = content.replace('\r\n', '\n')
            
            # register_blueprint(xxx_bp, url_prefix='/xxx') を検出
            register_pattern = r'register_blueprint\s*\(\s*(\w+)\s*(?:,\s*url_prefix\s*=\s*[\'"]([^\'"]*)[\'"]\s*)?\)'
            for match in re.finditer(register_pattern, content):
                bp_var = match.group(1)  # blueprint変数名 (例: inventory_bp)
                url_prefix = match.group(2) if match.group(2) else ''
                blueprint_prefixes[bp_var] = url_prefix
        
        # Step 2: 各ファイルからBlueprint定義とルートを収集
        all_routes = []
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # Blueprint定義を検出: xxx_bp = Blueprint(...)
            bp_var_in_file = None
            bp_def_match = re.search(r'(\w+)\s*=\s*Blueprint\s*\(', content)
            if bp_def_match:
                bp_var_in_file = bp_def_match.group(1)
            
            # このファイルのBlueprintのurl_prefixを取得
            file_prefix = ''
            if bp_var_in_file and bp_var_in_file in blueprint_prefixes:
                file_prefix = blueprint_prefixes[bp_var_in_file]
            
            # ルートデコレータを検出
            route_pattern = r'@(\w+)\.route\s*\(\s*[\'"]([^\'"]+)[\'"](?:.*?methods\s*=\s*\[([^\]]+)\])?'
            for match in re.finditer(route_pattern, content, re.DOTALL):
                decorator_bp = match.group(1)  # デコレータのblueprint名 (例: inventory_bp)
                route_path = match.group(2)
                methods_str = match.group(3) if match.group(3) else "'GET'"
                
                # メソッドを解析
                methods = re.findall(r"['\"](\w+)['\"]", methods_str)
                if not methods:
                    methods = ['GET']
                
                line_no = content[:match.start()].count('\n') + 1
                
                # url_prefixを決定（デコレータのbp名 > ファイル内のbp定義）
                route_prefix = ''
                if decorator_bp in blueprint_prefixes:
                    route_prefix = blueprint_prefixes[decorator_bp]
                elif file_prefix:
                    route_prefix = file_prefix
                
                full_route = route_prefix + route_path
                
                for method in methods:
                    all_routes.append({
                        'file': filepath,
                        'line': line_no,
                        'method': method.upper(),
                        'route': full_route,
                        'original_route': route_path,
                        'prefix': route_prefix
                    })
        
        # Step 3: 重複チェック
        route_map = {}  # (method, route) -> [route_info, ...]
        for route_info in all_routes:
            key = (route_info['method'], route_info['route'])
            if key not in route_map:
                route_map[key] = []
            route_map[key].append(route_info)
        
        for (method, route), route_infos in route_map.items():
            if len(route_infos) > 1:
                # 同じファイル内の重複は無視（デコレータの重複検出誤り防止）
                unique_files = set(r['file'] for r in route_infos)
                if len(unique_files) <= 1:
                    continue
                
                # 重複発見
                for i, info in enumerate(route_infos):
                    other_files = [r['file'] for j, r in enumerate(route_infos) if j != i]
                    issues.append({
                        'type': 'flask_route_duplicate',
                        'file': info['file'],
                        'line': info['line'],
                        'method': method,
                        'route': route,
                        'original_route': info['original_route'],
                        'conflicting_files': other_files,
                        'details': f"Route '{method} {route}' is duplicated in {', '.join(other_files)}"
                    })
                    logger.debug(f"[G-13] {info['file']}:{info['line']} - Duplicate route '{method} {route}'")
        
        if issues:
            logger.debug(f"[G-13] Found {len(issues)} Flask route duplicate issues")
        else:
            logger.debug("[G-13] No Flask route duplicates found")
        
        return issues
    
    def _auto_fix_flask_route_duplicates(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Flaskルート重複をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        # ファイルごとにグループ化（最初のファイル以外を修正対象とする）
        seen_routes = set()
        files_to_fix = {}
        
        for issue in issues:
            route_key = (issue['method'], issue['route'])
            if route_key in seen_routes:
                # 2つ目以降の重複ルートを修正対象に
                filepath = issue['file']
                if filepath not in files_to_fix:
                    files_to_fix[filepath] = []
                files_to_fix[filepath].append(issue)
            else:
                seen_routes.add(route_key)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: Route '{issue['method']} {issue['route']}' conflicts with {issue['conflicting_files']}"
                )
            
            # 変更前のルートを記録
            old_routes = {issue['route'] for issue in file_issues}
            
            prompt = f"""Fix duplicate Flask route issues in this file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Rename the duplicate routes to unique paths (e.g., add a suffix or change the path structure)
2. Update any internal references to these routes
3. Keep the same functionality, just change the route path
4. Do NOT remove routes, only rename them

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-13] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a Flask routing expert. Fix duplicate routes while preserving functionality.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-13] ✅ Fixed {filepath}")
                        
                        # フロントエンドのfetch URLも更新
                        # 新しいルートを抽出
                        new_routes = set()
                        route_pattern = r"@\w+\.route\s*\(\s*['\"]([^'\"]+)['\"]"
                        for match in re.finditer(route_pattern, fixed_code):
                            new_routes.add(match.group(1))
                        
                        # 古いルート → 新しいルートのマッピングを推測
                        # (同じBlueprintのurl_prefixを考慮)
                        for old_route in old_routes:
                            for new_route in new_routes:
                                # 古いルートが新しいルートの部分文字列でない場合、変更された可能性
                                if old_route != new_route and old_route not in new_routes:
                                    # フロントエンドファイルを更新
                                    for js_path, js_content in generated_files.items():
                                        if js_path.endswith(('.js', '.jsx', '.ts', '.tsx', '.html')):
                                            # Blueprint url_prefixを含む完全パスを推測
                                            # fetch('/game/saves') のようなパターン
                                            updated_content = js_content
                                            
                                            # 単純なルートパス置換
                                            updated_content = updated_content.replace(
                                                f"'{old_route}'",
                                                f"'{new_route}'"
                                            )
                                            updated_content = updated_content.replace(
                                                f'"{old_route}"',
                                                f'"{new_route}"'
                                            )
                                            updated_content = updated_content.replace(
                                                f"`{old_route}",
                                                f"`{new_route}"
                                            )
                                            
                                            if updated_content != js_content:
                                                generated_files[js_path] = updated_content
                                                logger.debug(f"[G-13] Updated frontend URL in {js_path}: {old_route} → {new_route}")
                    else:
                        logger.debug(f"[G-13] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-13] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-13] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-14: SQLAlchemyテーブル名重複チェック
    # ============================================
    
    def _validate_sqlalchemy_table_duplicates(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemyテーブル名の重複を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # 全テーブル定義を収集
        all_tables = []  # [(filepath, line_no, class_name, table_name)]
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応（Windows改行コード）
            content = content.replace('\r\n', '\n')
            
            # __tablename__ 定義を検出
            pattern = r'class\s+(\w+)\s*\([^)]*(?:db\.Model|Base)[^)]*\)\s*:.*?__tablename__\s*=\s*[\'"]([^\'"]+)[\'"]'
            for match in re.finditer(pattern, content, re.DOTALL):
                class_name = match.group(1)
                table_name = match.group(2)
                line_no = content[:match.start()].count('\n') + 1
                
                all_tables.append({
                    'file': filepath,
                    'line': line_no,
                    'class': class_name,
                    'table': table_name
                })
        
        # 重複チェック
        table_map = {}  # table_name -> [table_info, ...]
        for table_info in all_tables:
            table_name = table_info['table']
            if table_name not in table_map:
                table_map[table_name] = []
            table_map[table_name].append(table_info)
        
        for table_name, table_infos in table_map.items():
            if len(table_infos) > 1:
                for i, info in enumerate(table_infos):
                    other_classes = [f"{t['class']} in {t['file']}" for j, t in enumerate(table_infos) if j != i]
                    issues.append({
                        'type': 'sqlalchemy_table_duplicate',
                        'file': info['file'],
                        'line': info['line'],
                        'class': info['class'],
                        'table': table_name,
                        'conflicting_classes': other_classes,
                        'details': f"Table '{table_name}' is also defined in {', '.join(other_classes)}"
                    })
                    logger.debug(f"[G-14] {info['file']}:{info['line']} - Duplicate table '{table_name}'")
        
        if issues:
            logger.debug(f"[G-14] Found {len(issues)} SQLAlchemy table duplicate issues")
        else:
            logger.debug("[G-14] No SQLAlchemy table duplicates found")
        
        return issues
    
    def _auto_fix_sqlalchemy_table_duplicates(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        SQLAlchemyテーブル名重複をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        # 重複テーブルの最初以外を修正
        seen_tables = set()
        files_to_fix = {}
        
        for issue in issues:
            table_name = issue['table']
            if table_name in seen_tables:
                filepath = issue['file']
                if filepath not in files_to_fix:
                    files_to_fix[filepath] = []
                files_to_fix[filepath].append(issue)
            else:
                seen_tables.add(table_name)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Class '{issue['class']}': Table name '{issue['table']}' conflicts with {issue['conflicting_classes']}"
                )
            
            prompt = f"""Fix duplicate SQLAlchemy table name issues in this file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Rename the __tablename__ to a unique name (e.g., add a prefix or suffix based on context)
2. Update any ForeignKey references that point to this table
3. Keep the same class structure and functionality

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-14] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy expert. Fix duplicate table names while preserving relationships.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-14] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-14] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-14] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-14] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-15: JavaScript予約語チェック
    # ============================================
    
    def _validate_javascript_reserved_words(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        JavaScriptの予約語を変数名として使用している箇所を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # JavaScript予約語（変数名として使用不可または危険）
        JS_RESERVED_WORDS = {
            # 厳格モード予約語
            'arguments': 'args',
            'eval': 'evalFunc',
            'implements': 'implementsInterface',
            'interface': 'interfaceObj',
            'package': 'packageObj',
            'private': 'privateField',
            'protected': 'protectedField',
            'public': 'publicField',
            'static': 'staticField',
            'yield': 'yieldValue',
            'let': 'letVar',
            'await': 'awaitResult',
            # 将来の予約語
            'enum': 'enumValue',
            # よく誤用される名前
            'class': 'className',
            'function': 'funcObj',
            'delete': 'deleteItem',
            'export': 'exportData',
            'import': 'importData',
            'super': 'superClass',
            'extends': 'extendsClass',
            'default': 'defaultValue',
            'case': 'caseValue',
            'catch': 'catchError',
            'finally': 'finallyBlock',
            'throw': 'throwError',
            'try': 'tryBlock',
            'debugger': 'debuggerTool',
            'with': 'withObj',
        }
        
        for filepath, content in generated_files.items():
            if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                continue
            
            # CRLF対応（Windows改行コード）
            content = content.replace('\r\n', '\n')
            
            for reserved_word, suggested_name in JS_RESERVED_WORDS.items():
                # 変数宣言パターン: var/let/const reserved_word =
                var_pattern = rf'\b(var|let|const)\s+{reserved_word}\s*='
                for match in re.finditer(var_pattern, content):
                    line_no = content[:match.start()].count('\n') + 1
                    line_content = content.split('\n')[line_no - 1].strip()
                    
                    issues.append({
                        'type': 'javascript_reserved_word',
                        'file': filepath,
                        'line': line_no,
                        'reserved_word': reserved_word,
                        'suggested_name': suggested_name,
                        'code': line_content,
                        'details': f"'{reserved_word}' is a reserved word in JavaScript"
                    })
                    logger.debug(f"[G-15] {filepath}:{line_no} - JS reserved word '{reserved_word}'")
                
                # 関数パラメータパターン: function xxx(reserved_word) または (reserved_word) =>
                param_pattern = rf'(?:function\s+\w*\s*\([^)]*\b{reserved_word}\b[^)]*\)|\([^)]*\b{reserved_word}\b[^)]*\)\s*=>)'
                for match in re.finditer(param_pattern, content):
                    line_no = content[:match.start()].count('\n') + 1
                    line_content = content.split('\n')[line_no - 1].strip()
                    
                    # 重複チェック
                    existing = [i for i in issues if i['file'] == filepath and i['line'] == line_no and i['reserved_word'] == reserved_word]
                    if not existing:
                        issues.append({
                            'type': 'javascript_reserved_word_param',
                            'file': filepath,
                            'line': line_no,
                            'reserved_word': reserved_word,
                            'suggested_name': suggested_name,
                            'code': line_content,
                            'details': f"'{reserved_word}' used as parameter name"
                        })
                        logger.debug(f"[G-15] {filepath}:{line_no} - JS reserved word as param '{reserved_word}'")
        
        if issues:
            logger.debug(f"[G-15] Found {len(issues)} JavaScript reserved word issues")
        else:
            logger.debug("[G-15] No JavaScript reserved word issues found")
        
        return issues
    
    def _auto_fix_javascript_reserved_words(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        JavaScript予約語の問題をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        files_to_fix = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: Rename '{issue['reserved_word']}' to '{issue['suggested_name']}'"
                )
            
            prompt = f"""Fix JavaScript reserved word issues in this file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```javascript
{original_code}
```

**Instructions:**
1. Rename the reserved word variables/parameters to the suggested names
2. Update ALL references to these renamed variables within this file
3. Do NOT change any other code or logic

Return ONLY the fixed JavaScript code wrapped in ```javascript``` blocks.
"""
            
            try:
                logger.debug(f"[G-15] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a JavaScript code refactoring expert. Rename reserved words while preserving functionality.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```(?:javascript|js)\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-15] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-15] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-15] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-15] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-16: SQLAlchemy relationship参照先検証
    # ============================================
    
    def _validate_relationship_references(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemy relationship()の参照先が有効なModelクラスか検証
        
        検出対象:
        - relationship参照先が存在しないクラス
        - relationship参照先がEnumなど非Modelクラス
        - back_populatesの不整合
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # Step 1: 全Modelクラスを収集
        model_classes = set()
        enum_classes = set()
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応
            content = content.replace('\r\n', '\n')
            
            # db.Model または Base を継承したクラスを検出
            model_pattern = r'class\s+(\w+)\s*\([^)]*(?:db\.Model|Base)[^)]*\)\s*:'
            for match in re.finditer(model_pattern, content):
                model_classes.add(match.group(1))
            
            # Enumクラスを検出（from enum import Enum または PyEnum）
            enum_pattern = r'class\s+(\w+)\s*\([^)]*(?:Enum|PyEnum|IntEnum|StrEnum)[^)]*\)\s*:'
            for match in re.finditer(enum_pattern, content):
                enum_classes.add(match.group(1))
        
        logger.debug(f"[G-16] Found Model classes: {model_classes}")
        logger.debug(f"[G-16] Found Enum classes: {enum_classes}")
        
        # Step 2: relationship()の参照先を検証
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # CRLF対応
            content = content.replace('\r\n', '\n')
            
            # relationship('ClassName') または relationship("ClassName") を検出
            rel_pattern = r'relationship\s*\(\s*[\'"](\w+)[\'"]'
            for match in re.finditer(rel_pattern, content):
                ref_class = match.group(1)
                line_no = content[:match.start()].count('\n') + 1
                line_content = content.split('\n')[line_no - 1].strip()
                
                # 参照先がEnumの場合
                if ref_class in enum_classes:
                    issues.append({
                        'type': 'relationship_references_enum',
                        'file': filepath,
                        'line': line_no,
                        'referenced_class': ref_class,
                        'code': line_content,
                        'details': f"relationship() references Enum '{ref_class}', not a Model class",
                        'suggested_fix': f"Change to the correct Model class (e.g., if this is for quests, use 'Quest' instead of '{ref_class}')"
                    })
                    logger.debug(f"[G-16] {filepath}:{line_no} - relationship references Enum '{ref_class}'")
                
                # 参照先がModelでもEnumでもない（存在しないクラス）
                elif ref_class not in model_classes and ref_class not in enum_classes:
                    # TYPE_CHECKINGブロック内の前方参照は許容
                    # ただし、Modelクラスリストにないものは警告
                    issues.append({
                        'type': 'relationship_references_unknown',
                        'file': filepath,
                        'line': line_no,
                        'referenced_class': ref_class,
                        'code': line_content,
                        'details': f"relationship() references unknown class '{ref_class}'",
                        'suggested_fix': f"Verify that '{ref_class}' is a valid Model class and is imported correctly"
                    })
                    logger.debug(f"[G-16] {filepath}:{line_no} - relationship references unknown class '{ref_class}'")
        
        # Step 3: back_populatesの整合性検証
        back_populates_map = {}  # {(class_name, attr_name): (filepath, line_no, target_class, target_attr)}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # 現在のクラス名を追跡
            current_class = None
            for i, line in enumerate(content.split('\n'), 1):
                class_match = re.match(r'class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                
                # relationship with back_populates
                # パターン1: type annotation形式 (attr: Mapped[...] = relationship(...))
                bp_match = re.search(r'(\w+)\s*:.*=\s*relationship\s*\(\s*[\'"](\w+)[\'"].*back_populates\s*=\s*[\'"](\w+)[\'"]', line)
                if not bp_match:
                    # パターン2: 直接代入形式 (attr = relationship(...))
                    bp_match = re.search(r'(\w+)\s*=\s*relationship\s*\(\s*[\'"](\w+)[\'"].*back_populates\s*=\s*[\'"](\w+)[\'"]', line)
                if bp_match and current_class:
                    attr_name = bp_match.group(1)
                    target_class = bp_match.group(2)
                    target_attr = bp_match.group(3)
                    back_populates_map[(current_class, attr_name)] = (filepath, i, target_class, target_attr)
        
        # back_populatesの相互参照を検証
        # 各クラスの属性を収集（relationship以外も含む）
        class_attributes = {}  # {class_name: {attr_names}}
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            content = content.replace('\r\n', '\n')
            current_class = None
            for line in content.split('\n'):
                class_match = re.match(r'class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                    if current_class not in class_attributes:
                        class_attributes[current_class] = set()
                
                # 属性定義を検出（Mapped[...] = または = relationship/Column等）
                # パターン1: type annotation形式 (attr: Mapped[...] = ...)
                if current_class:
                    attr_match = re.match(r'^\s+(\w+)\s*:', line)
                    if attr_match and not attr_match.group(1).startswith('_'):
                        class_attributes[current_class].add(attr_match.group(1))
                    # パターン2: 直接代入形式 (attr = ...)
                    else:
                        attr_match2 = re.match(r'^\s+(\w+)\s*=', line)
                        if attr_match2 and not attr_match2.group(1).startswith('_'):
                            class_attributes[current_class].add(attr_match2.group(1))
        
        for (class_name, attr_name), (filepath, line_no, target_class, target_attr) in back_populates_map.items():
            # 相手側の定義を探す
            expected_key = (target_class, target_attr)
            if expected_key in back_populates_map:
                reverse_info = back_populates_map[expected_key]
                reverse_target_class = reverse_info[2]
                reverse_target_attr = reverse_info[3]
                
                # 相手側が自分を指していない場合
                if reverse_target_class != class_name or reverse_target_attr != attr_name:
                    issues.append({
                        'type': 'back_populates_mismatch',
                        'file': filepath,
                        'line': line_no,
                        'class': class_name,
                        'attr': attr_name,
                        'target_class': target_class,
                        'target_attr': target_attr,
                        'details': f"back_populates mismatch: {class_name}.{attr_name} -> {target_class}.{target_attr}, but {target_class}.{target_attr} -> {reverse_target_class}.{reverse_target_attr}",
                        'suggested_fix': f"Ensure {target_class}.{target_attr} has back_populates='{attr_name}'"
                    })
                    logger.debug(f"[G-16] {filepath}:{line_no} - back_populates mismatch")
            else:
                # 相手側にback_populatesがない場合、属性自体が存在するかチェック
                target_attrs = class_attributes.get(target_class, set())
                if target_attr not in target_attrs:
                    issues.append({
                        'type': 'back_populates_target_missing',
                        'file': filepath,
                        'line': line_no,
                        'class': class_name,
                        'attr': attr_name,
                        'target_class': target_class,
                        'target_attr': target_attr,
                        'details': f"back_populates target missing: {class_name}.{attr_name} references {target_class}.{target_attr}, but '{target_attr}' does not exist in {target_class}",
                        'suggested_fix': f"Add 'player_inventories: Mapped[List[\"{class_name}\"]] = relationship(\"{class_name}\", back_populates=\"{attr_name}\")' to {target_class}"
                    })
                    logger.debug(f"[G-16] {filepath}:{line_no} - back_populates target '{target_attr}' missing in {target_class}")
        
        if issues:
            logger.debug(f"[G-16] Found {len(issues)} relationship reference issues")
        else:
            logger.debug("[G-16] No relationship reference issues found")
        
        return issues
    
    def _auto_fix_relationship_references(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        relationship参照先の問題をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        # クラス名からファイルパスを特定するマップを構築
        class_to_file = {}
        for filepath, content in generated_files.items():
            if filepath.endswith('.py'):
                content = content.replace('\r\n', '\n')
                model_pattern = r'class\s+(\w+)\s*\([^)]*(?:db\.Model|Base)[^)]*\)\s*:'
                for match in re.finditer(model_pattern, content):
                    class_to_file[match.group(1)] = filepath
        
        # ファイルごとにグループ化（back_populates_target_missingは修正対象ファイルを変更）
        files_to_fix = {}
        for issue in issues:
            if issue['type'] == 'back_populates_target_missing':
                # 修正対象は target_class のファイル
                target_class = issue.get('target_class', '')
                target_filepath = class_to_file.get(target_class)
                if target_filepath:
                    if target_filepath not in files_to_fix:
                        files_to_fix[target_filepath] = []
                    files_to_fix[target_filepath].append(issue)
            else:
                filepath = issue['file']
                if filepath not in files_to_fix:
                    files_to_fix[filepath] = []
                files_to_fix[filepath].append(issue)
        
        # 全Modelクラス情報を収集
        model_info = []
        for filepath, content in generated_files.items():
            if filepath.endswith('.py'):
                content = content.replace('\r\n', '\n')
                model_pattern = r'class\s+(\w+)\s*\([^)]*(?:db\.Model|Base)[^)]*\)\s*:'
                for match in re.finditer(model_pattern, content):
                    model_info.append(f"- {match.group(1)} (in {filepath})")
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                if issue['type'] == 'relationship_references_enum':
                    fixes_info.append(
                        f"- Line {issue['line']}: relationship('{issue['referenced_class']}') references an Enum, not a Model. "
                        f"Change to the correct Model class."
                    )
                elif issue['type'] == 'relationship_references_unknown':
                    fixes_info.append(
                        f"- Line {issue['line']}: relationship('{issue['referenced_class']}') references unknown class. "
                        f"Verify and fix the class name."
                    )
                elif issue['type'] == 'back_populates_mismatch':
                    fixes_info.append(
                        f"- Line {issue['line']}: back_populates mismatch for {issue['class']}.{issue['attr']}. "
                        f"{issue['details']}"
                    )
                elif issue['type'] == 'back_populates_target_missing':
                    fixes_info.append(
                        f"- {issue['target_class']} is missing the '{issue['target_attr']}' relationship property. "
                        f"Add: {issue['target_attr']}: Mapped[List[\"{issue['class']}\"]] = relationship(\"{issue['class']}\", back_populates=\"{issue['attr']}\")"
                    )
            
            prompt = f"""Fix the SQLAlchemy relationship reference issues in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Available Model classes in this project:**
{chr(10).join(model_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Fix relationship() to reference the correct Model class (not Enum)
2. For example, if a Player has quests, the relationship should be relationship('Quest'), not relationship('QuestStatus')
3. Ensure back_populates attributes match on both sides of the relationship
4. Update the Mapped type hints to match (e.g., Mapped[list["Quest"]] not Mapped[list["QuestStatus"]])
5. Do NOT change any other code or logic

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-16] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy expert. Fix relationship references to point to valid Model classes.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-16] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-16] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-16] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-16] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-17: ForeignKey参照先検証
    # ============================================
    
    def _validate_foreignkey_references(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        ForeignKey()の参照先テーブルが存在するか検証
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # Step 1: 全テーブル名を収集
        table_names = set()
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # __tablename__ を検出
            tablename_pattern = r"__tablename__\s*=\s*['\"]([^'\"]+)['\"]"
            for match in re.finditer(tablename_pattern, content):
                table_names.add(match.group(1))
        
        logger.debug(f"[G-17] Found tables: {table_names}")
        
        # Step 2: ForeignKey参照先を検証
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # ForeignKey('table.column') または ForeignKey("table.column") を検出
            fk_pattern = r"ForeignKey\s*\(\s*['\"]([^'\"]+)['\"]"
            for match in re.finditer(fk_pattern, content):
                fk_ref = match.group(1)
                # table.column 形式から table 部分を抽出
                if '.' in fk_ref:
                    ref_table = fk_ref.split('.')[0]
                else:
                    ref_table = fk_ref
                
                line_no = content[:match.start()].count('\n') + 1
                line_content = content.split('\n')[line_no - 1].strip()
                
                if ref_table not in table_names:
                    issues.append({
                        'type': 'foreignkey_references_unknown_table',
                        'file': filepath,
                        'line': line_no,
                        'referenced_table': ref_table,
                        'full_reference': fk_ref,
                        'code': line_content,
                        'details': f"ForeignKey references unknown table '{ref_table}'",
                        'available_tables': list(table_names)
                    })
                    logger.debug(f"[G-17] {filepath}:{line_no} - ForeignKey references unknown table '{ref_table}'")
        
        if issues:
            logger.debug(f"[G-17] Found {len(issues)} ForeignKey reference issues")
        else:
            logger.debug("[G-17] No ForeignKey reference issues found")
        
        return issues
    
    def _auto_fix_foreignkey_references(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        ForeignKey参照先の問題をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        files_to_fix = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            available_tables = file_issues[0].get('available_tables', [])
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: ForeignKey('{issue['full_reference']}') references unknown table '{issue['referenced_table']}'"
                )
            
            prompt = f"""Fix the ForeignKey reference issues in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Available tables in this project:**
{', '.join(available_tables)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Fix ForeignKey() to reference an existing table
2. Ensure the column name after the dot is correct (usually 'id')
3. Do NOT change any other code or logic

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-17] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy expert. Fix ForeignKey references to point to valid tables.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-17] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-17] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-17] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-17] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-18: SQLAlchemy Enum定義検証
    # ============================================
    
    def _validate_sqlalchemy_enum_usage(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemy Enumの使用方法が正しいか検証
        
        検出対象:
        - Enum()に渡されたクラスがPython Enumを継承していない
        - Enumカラムの定義ミス
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # Step 1: 全Enumクラスを収集
        enum_classes = set()
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # Python Enumを継承したクラスを検出
            enum_pattern = r'class\s+(\w+)\s*\([^)]*(?:Enum|PyEnum|IntEnum|StrEnum)[^)]*\)\s*:'
            for match in re.finditer(enum_pattern, content):
                enum_classes.add(match.group(1))
        
        logger.debug(f"[G-18] Found Enum classes: {enum_classes}")
        
        # Step 2: Enum()の使用を検証
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # Enum(ClassName) を検出（SQLAlchemy Enum型）
            # パターン: Enum(ClassName) または Enum('value1', 'value2')
            enum_usage_pattern = r'Enum\s*\(\s*(\w+)\s*[,)]'
            for match in re.finditer(enum_usage_pattern, content):
                ref_class = match.group(1)
                line_no = content[:match.start()].count('\n') + 1
                line_content = content.split('\n')[line_no - 1].strip()
                
                # 文字列リテラルの場合はスキップ（Enum('active', 'inactive')形式）
                if ref_class.startswith("'") or ref_class.startswith('"'):
                    continue
                
                # 参照先がEnumクラスでない場合
                if ref_class not in enum_classes:
                    # 一般的なPython型名はスキップ
                    if ref_class in ['str', 'int', 'float', 'bool', 'String', 'Integer']:
                        continue
                    
                    issues.append({
                        'type': 'enum_references_non_enum',
                        'file': filepath,
                        'line': line_no,
                        'referenced_class': ref_class,
                        'code': line_content,
                        'details': f"Enum() references '{ref_class}' which is not a Python Enum class",
                        'available_enums': list(enum_classes)
                    })
                    logger.debug(f"[G-18] {filepath}:{line_no} - Enum references non-Enum class '{ref_class}'")
        
        if issues:
            logger.debug(f"[G-18] Found {len(issues)} Enum usage issues")
        else:
            logger.debug("[G-18] No Enum usage issues found")
        
        return issues
    
    def _auto_fix_sqlalchemy_enum_usage(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Enum使用の問題をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        files_to_fix = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            available_enums = file_issues[0].get('available_enums', [])
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: Enum({issue['referenced_class']}) references a non-Enum class"
                )
            
            prompt = f"""Fix the SQLAlchemy Enum usage issues in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Available Python Enum classes in this project:**
{', '.join(available_enums)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Fix Enum() to reference a valid Python Enum class
2. Or create a new Enum class if needed
3. Ensure proper import of the Enum class
4. Do NOT change any other code or logic

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-18] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy expert. Fix Enum usage to reference valid Python Enum classes.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-18] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-18] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-18] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-18] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-19: Mapped型アノテーション整合性検証
    # ============================================
    
    def _validate_mapped_type_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Mapped型アノテーションとmapped_column/Column定義の整合性を検証
        
        検出対象:
        - Mapped[int]なのにString型カラム
        - Mapped[str]なのにInteger型カラム
        - 型の不一致
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # 型マッピング: Python型 -> 許容されるSQLAlchemy型
        TYPE_MAPPING = {
            'int': ['Integer', 'BigInteger', 'SmallInteger', 'primary_key=True'],
            'str': ['String', 'Text', 'VARCHAR', 'CHAR'],
            'float': ['Float', 'Numeric', 'Decimal', 'REAL'],
            'bool': ['Boolean', 'BOOLEAN'],
            'datetime': ['DateTime', 'DATETIME', 'TIMESTAMP'],
            'date': ['Date', 'DATE'],
            'time': ['Time', 'TIME'],
            'bytes': ['LargeBinary', 'BLOB', 'BINARY'],
        }
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            
            # Mapped[Type] = mapped_column(...) パターンを検出
            # 例: name: Mapped[str] = mapped_column(Integer, ...)
            pattern = r'(\w+)\s*:\s*Mapped\[([^\]]+)\]\s*=\s*mapped_column\s*\(([^)]+)\)'
            
            for match in re.finditer(pattern, content):
                attr_name = match.group(1)
                mapped_type = match.group(2).strip()
                column_def = match.group(3).strip()
                
                line_no = content[:match.start()].count('\n') + 1
                line_content = content.split('\n')[line_no - 1].strip()
                
                # Optional[Type] や list[Type] から基本型を抽出
                base_type = mapped_type
                if 'Optional[' in mapped_type:
                    base_type = re.search(r'Optional\[(\w+)\]', mapped_type)
                    base_type = base_type.group(1) if base_type else mapped_type
                elif 'list[' in mapped_type.lower():
                    continue  # リレーションシップなのでスキップ
                
                # 型チェック
                base_type_lower = base_type.lower()
                if base_type_lower in TYPE_MAPPING:
                    allowed_types = TYPE_MAPPING[base_type_lower]
                    
                    # カラム定義に許容される型が含まれているかチェック
                    type_found = False
                    for allowed_type in allowed_types:
                        if allowed_type in column_def:
                            type_found = True
                            break
                    
                    if not type_found:
                        # 不一致の可能性をチェック
                        for py_type, sql_types in TYPE_MAPPING.items():
                            if py_type != base_type_lower:
                                for sql_type in sql_types:
                                    if sql_type in column_def and sql_type != 'primary_key=True':
                                        issues.append({
                                            'type': 'mapped_type_mismatch',
                                            'file': filepath,
                                            'line': line_no,
                                            'attribute': attr_name,
                                            'mapped_type': mapped_type,
                                            'column_type': sql_type,
                                            'code': line_content,
                                            'details': f"Mapped[{mapped_type}] but column uses {sql_type}",
                                            'suggested_mapped_type': py_type
                                        })
                                        logger.debug(f"[G-19] {filepath}:{line_no} - Type mismatch: Mapped[{mapped_type}] vs {sql_type}")
                                        break
        
        if issues:
            logger.debug(f"[G-19] Found {len(issues)} Mapped type consistency issues")
        else:
            logger.debug("[G-19] No Mapped type consistency issues found")
        
        return issues
    
    def _auto_fix_mapped_type_consistency(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Mapped型の不整合をLLMで自動修正
        """
        if not issues or not self.llm_manager:
            return generated_files
        
        files_to_fix = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in files_to_fix:
                files_to_fix[filepath] = []
            files_to_fix[filepath].append(issue)
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Line {issue['line']}: {issue['attribute']} has Mapped[{issue['mapped_type']}] but column uses {issue['column_type']}. "
                    f"Either change to Mapped[{issue['suggested_mapped_type']}] or change column type."
                )
            
            prompt = f"""Fix the Mapped type annotation inconsistencies in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Fix the type mismatch by either:
   a. Changing the Mapped[Type] annotation to match the column type, OR
   b. Changing the column type to match the Mapped annotation
2. Choose the option that makes more semantic sense for the attribute name
3. Do NOT change any other code or logic

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-19] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy expert. Fix type annotation mismatches between Mapped[] and column definitions.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-19] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-19] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[G-19] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-19] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ==========================================
    # G-20: Flask静的パス検証
    # ==========================================
    def _validate_flask_static_paths(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Flask静的ファイルパスの整合性を検証
        
        Flaskプロジェクトで、HTML内の静的ファイル参照が
        /static/ プレフィックスを使用しているか検証する。
        
        Args:
            generated_files: 生成されたファイル辞書 {filepath: content}
        
        Returns:
            問題のリスト [{file, line, issue, current_path, suggested_path}]
        """
        issues = []
        
        # Flaskプロジェクト判定
        is_flask_project = False
        for filepath, content in generated_files.items():
            if filepath.endswith('.py'):
                if 'from flask import' in content or 'import flask' in content.lower():
                    is_flask_project = True
                    break
                if 'Flask(' in content or 'flask.Flask' in content:
                    is_flask_project = True
                    break
        
        if not is_flask_project:
            logger.debug("[G-20] Not a Flask project, skipping static path validation")
            return issues
        
        logger.debug("[G-20] Flask project detected, validating static paths")
        
        # HTML/JSファイルの静的パス参照をチェック
        for filepath, content in generated_files.items():
            if not filepath.endswith(('.html', '.htm')):
                continue
            
            lines = content.split('\n')
            for line_num, line in enumerate(lines, 1):
                # CSS参照: href="..."
                css_matches = re.findall(
                    r'<link[^>]+href=["\']([^"\']+\.css)["\']',
                    line, re.IGNORECASE
                )
                for css_path in css_matches:
                    # 既に/static/で始まる場合はOK
                    if css_path.startswith('/static/') or css_path.startswith('http'):
                        continue
                    # {{ url_for(...) }} の場合はOK
                    if '{{' in css_path or '{%' in css_path:
                        continue
                    # 相対パス → 問題
                    issues.append({
                        'file': filepath,
                        'line': line_num,
                        'issue': 'CSS relative path in Flask project',
                        'current_path': css_path,
                        'suggested_path': f'/static/{css_path.lstrip("./")}'
                    })
                
                # JS参照: src="..."
                js_matches = re.findall(
                    r'<script[^>]+src=["\']([^"\']+\.js)["\']',
                    line, re.IGNORECASE
                )
                for js_path in js_matches:
                    if js_path.startswith('/static/') or js_path.startswith('http'):
                        continue
                    if '{{' in js_path or '{%' in js_path:
                        continue
                    issues.append({
                        'file': filepath,
                        'line': line_num,
                        'issue': 'JS relative path in Flask project',
                        'current_path': js_path,
                        'suggested_path': f'/static/{js_path.lstrip("./")}'
                    })
                
                # 画像参照: src="..."
                img_matches = re.findall(
                    r'<img[^>]+src=["\']([^"\']+\.(png|jpg|jpeg|gif|svg|webp))["\']',
                    line, re.IGNORECASE
                )
                for img_match in img_matches:
                    img_path = img_match[0] if isinstance(img_match, tuple) else img_match
                    if img_path.startswith('/static/') or img_path.startswith('http') or img_path.startswith('data:'):
                        continue
                    if '{{' in img_path or '{%' in img_path:
                        continue
                    issues.append({
                        'file': filepath,
                        'line': line_num,
                        'issue': 'Image relative path in Flask project',
                        'current_path': img_path,
                        'suggested_path': f'/static/{img_path.lstrip("./")}'
                    })
        
        if issues:
            logger.debug(f"[G-20] Found {len(issues)} Flask static path issues")
        else:
            logger.debug("[G-20] No Flask static path issues found")
        
        return issues
    
    def _auto_fix_flask_static_paths(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Flask静的パスの問題を自動修正
        
        相対パスを/static/付きの絶対パスに変換する。
        
        Args:
            issues: 検出された問題リスト
            generated_files: 生成されたファイル辞書
        
        Returns:
            修正後のファイル辞書
        """
        if not issues:
            return generated_files
        
        # ファイルごとにグループ化
        issues_by_file = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in issues_by_file:
                issues_by_file[filepath] = []
            issues_by_file[filepath].append(issue)
        
        # 各ファイルを修正
        for filepath, file_issues in issues_by_file.items():
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            
            for issue in file_issues:
                current_path = issue['current_path']
                suggested_path = issue['suggested_path']
                
                # パスを置換（クォート付きで正確に置換）
                content = content.replace(f'"{current_path}"', f'"{suggested_path}"')
                content = content.replace(f"'{current_path}'", f"'{suggested_path}'")
                
                logger.debug(f"[G-20] Fixed {filepath}: {current_path} → {suggested_path}")
            
            generated_files[filepath] = content
        
        return generated_files

    # ==========================================
    # G-21: HTML参照整合性検証
    # ==========================================
    def _validate_html_asset_references(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        HTML内のCSS/JS参照が実際のファイルと一致するか検証
        
        HTMLで参照されているCSS/JSファイルが生成されているか確認。
        
        Args:
            generated_files: 生成されたファイル辞書 {filepath: content}
        
        Returns:
            問題のリスト [{html_file, line, ref_type, ref_path, issue}]
        """
        issues = []
        
        # 生成されたファイルパスのセット（正規化）
        generated_paths = set()
        for filepath in generated_files.keys():
            # 複数の形式で登録（マッチしやすくするため）
            generated_paths.add(filepath)
            generated_paths.add(filepath.lstrip('./'))
            generated_paths.add('/' + filepath.lstrip('./'))
            # static/プレフィックス付きも追加
            if not filepath.startswith('static/'):
                generated_paths.add('static/' + filepath.lstrip('./'))
                generated_paths.add('/static/' + filepath.lstrip('./'))
        
        # HTMLファイルの参照をチェック
        for filepath, content in generated_files.items():
            if not filepath.endswith(('.html', '.htm')):
                continue
            
            lines = content.split('\n')
            for line_num, line in enumerate(lines, 1):
                # CSS参照
                css_matches = re.findall(
                    r'<link[^>]+href=["\']([^"\']+)["\'][^>]*>',
                    line, re.IGNORECASE
                )
                for href in css_matches:
                    # stylesheetのみチェック
                    if 'rel=' in line and 'stylesheet' not in line.lower():
                        continue
                    # 外部URLはスキップ
                    if href.startswith('http') or href.startswith('//'):
                        continue
                    # Jinja2テンプレートはスキップ
                    if '{{' in href or '{%' in href:
                        continue
                    # CDNはスキップ
                    if 'cdn' in href.lower() or 'googleapis' in href.lower():
                        continue
                    
                    # パスを正規化してチェック
                    normalized_href = href.lstrip('./')
                    if not self._is_asset_generated(normalized_href, generated_paths):
                        issues.append({
                            'html_file': filepath,
                            'line': line_num,
                            'ref_type': 'CSS',
                            'ref_path': href,
                            'issue': f'Referenced CSS file not generated: {href}'
                        })
                
                # JS参照
                js_matches = re.findall(
                    r'<script[^>]+src=["\']([^"\']+)["\'][^>]*>',
                    line, re.IGNORECASE
                )
                for src in js_matches:
                    # 外部URLはスキップ
                    if src.startswith('http') or src.startswith('//'):
                        continue
                    # Jinja2テンプレートはスキップ
                    if '{{' in src or '{%' in src:
                        continue
                    # CDNはスキップ
                    if 'cdn' in src.lower() or 'googleapis' in src.lower() or 'unpkg' in src.lower():
                        continue
                    
                    normalized_src = src.lstrip('./')
                    if not self._is_asset_generated(normalized_src, generated_paths):
                        issues.append({
                            'html_file': filepath,
                            'line': line_num,
                            'ref_type': 'JS',
                            'ref_path': src,
                            'issue': f'Referenced JS file not generated: {src}'
                        })
        
        if issues:
            logger.debug(f"[G-21] Found {len(issues)} missing asset references")
        else:
            logger.debug("[G-21] All asset references are valid")
        
        return issues
    
    def _is_asset_generated(self, ref_path: str, generated_paths: set) -> bool:
        """参照パスが生成ファイルに含まれるかチェック"""
        # 直接マッチ
        if ref_path in generated_paths:
            return True
        
        # /static/を除去してチェック
        if ref_path.startswith('/static/'):
            without_static = ref_path[8:]  # len('/static/') = 8
            if without_static in generated_paths:
                return True
        
        # static/を除去してチェック
        if ref_path.startswith('static/'):
            without_static = ref_path[7:]  # len('static/') = 7
            if without_static in generated_paths:
                return True
        
        # ファイル名のみでチェック（最後の手段）
        ref_filename = ref_path.split('/')[-1]
        for path in generated_paths:
            if path.endswith('/' + ref_filename) or path == ref_filename:
                return True
        
        return False
    
    def _auto_fix_html_asset_references(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        HTML参照整合性の問題を自動修正
        
        欠落しているCSS/JSファイルの生成をLLMに依頼する。
        
        Args:
            issues: 検出された問題リスト
            generated_files: 生成されたファイル辞書
        
        Returns:
            修正後のファイル辞書（追加ファイル含む）
        """
        if not issues:
            return generated_files
        
        # 欠落ファイルをグループ化
        missing_files = {}
        for issue in issues:
            ref_path = issue['ref_path']
            ref_type = issue['ref_type']
            
            # パスを正規化
            normalized_path = ref_path.lstrip('./')
            if normalized_path.startswith('/static/'):
                normalized_path = normalized_path[1:]  # 先頭の/を除去
            elif normalized_path.startswith('static/'):
                pass  # そのまま
            else:
                normalized_path = 'static/' + normalized_path
            
            if normalized_path not in missing_files:
                missing_files[normalized_path] = ref_type
        
        # 各欠落ファイルを生成
        for filepath, file_type in missing_files.items():
            if filepath in generated_files:
                continue
            
            logger.debug(f"[G-21] Generating missing {file_type} file: {filepath}")
            
            try:
                # 関連するHTMLファイルの内容を取得（コンテキスト用）
                html_context = ""
                for html_file, content in generated_files.items():
                    if html_file.endswith('.html'):
                        html_context = content[:2000]  # 最初の2000文字
                        break
                
                prompt = f"""Generate the missing {file_type} file for a web application.

File to generate: {filepath}

HTML context (for reference):
```html
{html_context}
```

Requirements:
1. Generate a complete, functional {file_type} file
2. The file should work with the HTML shown above
3. Use modern best practices
4. Include appropriate comments

Return ONLY the {file_type} code, no explanations.
"""
                
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt=f"You are a {file_type} expert. Generate clean, professional code.",
                    max_tokens=4000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                # コードブロックを抽出
                code_pattern = r'```(?:css|javascript|js)?\s*(.*?)\s*```'
                code_match = re.search(code_pattern, response_text, re.DOTALL)
                if code_match:
                    generated_code = code_match.group(1).strip()
                else:
                    # コードブロックがない場合は全体を使用
                    generated_code = response_text.strip()
                
                if len(generated_code) > 50:  # 最小限の内容があるかチェック
                    generated_files[filepath] = generated_code
                    logger.debug(f"[G-21] ✅ Generated missing file: {filepath}")
                else:
                    logger.debug(f"[G-21] ⚠ Generated content too short for {filepath}")
                
            except Exception as e:
                logger.debug(f"[G-21] ⚠ Error generating {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-22: JavaScriptアニメーションループパターン検証
    # ============================================
    
    def _validate_js_animation_loop(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        JavaScriptのアニメーションループパターンを検証
        gameLoop等のメソッドがrequestAnimationFrame経由で正しく呼ばれているかチェック
        
        検出パターン:
          1. animation loop関数が引数なしで直接呼び出されている
          2. requestAnimationFrameのcallback内でDate.now()とtimestamp引数が混在している
             （時間基準不一致バグ: DOMHighResTimeStamp vs Unix epoch ms）
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # アニメーションループ関連のメソッド名パターン
        LOOP_METHOD_NAMES = ['gameLoop', 'animationLoop', 'renderLoop', 'mainLoop', 'update', 'animate', 'loop', 'frame', 'tick']
        
        for filepath, content in generated_files.items():
            # .htmlファイルの場合、<script>タグ内のJSを抽出
            if filepath.endswith('.html'):
                script_blocks = re.findall(r'<script[^>]*>(.*?)</script>', content, re.DOTALL)
                if not script_blocks:
                    continue
                js_content = '\n'.join(script_blocks)
            elif filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                js_content = content
            else:
                continue
            
            # CRLF対応
            js_content = js_content.replace('\r\n', '\n')
            lines = js_content.split('\n')
            
            for method_name in LOOP_METHOD_NAMES:
                # メソッド定義を検出: gameLoop(timestamp) or function gameLoop(currentTime) 等
                method_def_pattern = rf'(?:function\s+)?{method_name}\s*\(\s*(\w+)\s*\)\s*{{'
                method_def_match = re.search(method_def_pattern, js_content)
                
                if not method_def_match:
                    continue
                
                param_name = method_def_match.group(1)
                
                # パラメータが時間関連の名前かチェック
                time_param_names = ['time', 'timestamp', 'currentTime', 'now', 't', 'dt', 'deltaTime']
                is_time_param = any(tp.lower() in param_name.lower() for tp in time_param_names)
                
                if not is_time_param:
                    continue
                
                # requestAnimationFrameで呼ばれているか確認
                raf_call_pattern = rf'requestAnimationFrame\s*\([^)]*{method_name}'
                is_raf_callback = re.search(raf_call_pattern, js_content) is not None
                
                if not is_raf_callback:
                    continue
                
                # ===== 検出1: 引数なしの直接呼び出し =====
                bad_call_pattern = rf'this\.{method_name}\s*\(\s*\)'
                good_call_pattern = rf'requestAnimationFrame\s*\([^)]*{method_name}'
                
                for line_no, line in enumerate(lines, 1):
                    stripped = line.strip()
                    if stripped.startswith('//') or stripped.startswith('/*'):
                        continue
                    if re.search(good_call_pattern, line):
                        continue
                    bad_match = re.search(bad_call_pattern, line)
                    if bad_match:
                        if 'requestAnimationFrame' not in line:
                            issues.append({
                                'type': 'animation_loop_bad_call',
                                'file': filepath,
                                'line': line_no,
                                'method_name': method_name,
                                'param_name': param_name,
                                'code': stripped,
                                'details': f"'{method_name}()' called without argument. Should use: requestAnimationFrame((t) => this.{method_name}(t))"
                            })
                            logger.debug(f"[G-22] {filepath}:{line_no} - Animation loop '{method_name}' called without timestamp argument")
                
                # ===== 検出2: 時間基準不一致（Date.now() vs rAF timestamp） =====
                # requestAnimationFrameのcallback引数(DOMHighResTimeStamp)を受け取る関数内で
                # タイムスタンプと比較される変数がDate.now()で代入されている場合を検出
                
                # 関数本体を抽出（ブレース対応）
                func_start = method_def_match.start()
                brace_count = 0
                func_body_start = js_content.index('{', func_start)
                func_body_end = func_body_start
                for ci in range(func_body_start, len(js_content)):
                    if js_content[ci] == '{':
                        brace_count += 1
                    elif js_content[ci] == '}':
                        brace_count -= 1
                        if brace_count == 0:
                            func_body_end = ci + 1
                            break
                
                func_body = js_content[func_body_start:func_body_end]
                
                # Step A: タイムスタンプ引数のエイリアスを特定
                # 例: const now = timestamp; → nowはtimestampのエイリアス
                aliases = {param_name}
                alias_pattern = rf'(?:const|let|var)\s+(\w+)\s*=\s*(?:{"|".join(re.escape(a) for a in aliases)})\b'
                for alias_match in re.finditer(alias_pattern, func_body):
                    aliases.add(alias_match.group(1))
                
                # Step B: エイリアスと差分比較される変数を特定
                # 例: now - lastDropTime → lastDropTimeが比較対象
                comparison_vars = set()
                for alias in aliases:
                    for m in re.finditer(rf'{re.escape(alias)}\s*-\s*(\w+)', func_body):
                        var = m.group(1)
                        if var not in aliases and not var.isdigit():
                            comparison_vars.add(var)
                    for m in re.finditer(rf'(\w+)\s*-\s*{re.escape(alias)}', func_body):
                        var = m.group(1)
                        if var not in aliases and not var.isdigit():
                            comparison_vars.add(var)
                
                # Step C: これらの変数がファイル全体でDate.now()で代入されているか検出
                for var in comparison_vars:
                    date_now_assigns = list(re.finditer(
                        rf'{re.escape(var)}\s*=\s*Date\.now\(\)',
                        js_content
                    ))
                    if date_now_assigns:
                        for dna in date_now_assigns:
                            dna_line = js_content[:dna.start()].count('\n') + 1
                            dna_code = js_content.split('\n')[dna_line - 1].strip()
                            # コメント行はスキップ
                            if dna_code.startswith('//') or dna_code.startswith('/*'):
                                continue
                            issues.append({
                                'type': 'timestamp_origin_mismatch',
                                'file': filepath,
                                'line': dna_line,
                                'method_name': method_name,
                                'param_name': param_name,
                                'var_name': var,
                                'code': dna_code,
                                'details': (
                                    f"'{var}' is assigned Date.now() (Unix epoch ms) but compared with "
                                    f"rAF timestamp (DOMHighResTimeStamp) in '{method_name}({param_name})'. "
                                    f"Replace Date.now() with performance.now(), or use timestamp "
                                    f"parameter consistently."
                                )
                            })
                            logger.debug(
                                f"[G-22] {filepath}:{dna_line} - Time origin mismatch: "
                                f"'{var} = Date.now()' but compared with rAF timestamp in {method_name}()"
                            )
        
        if issues:
            logger.debug(f"[G-22] Found {len(issues)} JavaScript animation loop issues")
        else:
            logger.debug("[G-22] No JavaScript animation loop issues found")
        
        return issues
    
    def _auto_fix_js_animation_loop(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        JavaScriptアニメーションループの問題を自動修正
        
        修正パターン:
          1. animation_loop_bad_call: this.gameLoop() → requestAnimationFrame((t) => this.gameLoop(t))
          2. timestamp_origin_mismatch: Date.now() → performance.now() (rAF callback内)
        """
        if not issues:
            return generated_files
        
        import re
        
        for issue in issues:
            filepath = issue['file']
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            method_name = issue['method_name']
            param_name = issue['param_name']
            
            if issue['type'] == 'animation_loop_bad_call':
                # 問題パターンを正しいパターンに置換
                # this.gameLoop(); → requestAnimationFrame((t) => this.gameLoop(t));
                bad_pattern = rf'(\s*)this\.{method_name}\s*\(\s*\)\s*;'
                good_replacement = rf'\1requestAnimationFrame((t) => this.{method_name}(t));'
                
                new_content = re.sub(bad_pattern, good_replacement, content)
                
                if new_content != content:
                    generated_files[filepath] = new_content
                    logger.debug(f"[G-22] ✅ Fixed animation loop call in {filepath}")
            
            elif issue['type'] == 'timestamp_origin_mismatch':
                # rAFタイムスタンプと比較される変数へのDate.now()代入をperformance.now()に置換
                var_name = issue.get('var_name', '')
                if not var_name:
                    continue
                
                # 該当変数へのDate.now()代入を置換
                pattern = rf'({re.escape(var_name)}\s*=\s*)Date\.now\(\)'
                replacement = rf'\1performance.now()'
                new_content = re.sub(pattern, replacement, content)
                
                if new_content != content:
                    generated_files[filepath] = new_content
                    content = new_content  # 次のissue処理用に更新
                    count = content.count('performance.now()') - content.count('performance.now()')
                    logger.debug(f"[G-22] ✅ Fixed '{var_name} = Date.now()' → '{var_name} = performance.now()' in {filepath}")
        
        return generated_files
        
        return generated_files

    # ============================================
    # G-23: API契約整合性チェック
    # ============================================
    
    def _validate_api_contract_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        フロントエンド↔バックエンド間のAPI契約整合性を検証
        
        フロントエンドで参照されているフィールドがバックエンドのスキーマに存在するか、
        また逆にスキーマで定義されているフィールドがフロントエンドで正しく参照されているかをチェック
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # 1. フロントエンドからAPI参照フィールドを抽出
        api_references = self._extract_frontend_api_field_references(generated_files)
        frontend_fields = api_references.get('_global', set())
        
        if not frontend_fields:
            logger.debug("[G-23] No frontend API references found")
            return issues
        
        # 2. バックエンドのスキーマフィールドを抽出
        schema_fields = {}  # {schema_name: {fields}}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            if 'schema' not in filepath.lower() and 'Schema' not in content:
                continue
            
            content = content.replace('\r\n', '\n')
            
            # Marshmallowスキーマクラスを検出
            class_pattern = r'class\s+(\w+Schema)\s*\([^)]*\)\s*:'
            for class_match in re.finditer(class_pattern, content):
                schema_name = class_match.group(1)
                class_start = class_match.end()
                
                # 次のクラスまたはファイル末尾までを取得
                next_class = re.search(r'\nclass\s+\w+', content[class_start:])
                if next_class:
                    class_body = content[class_start:class_start + next_class.start()]
                else:
                    class_body = content[class_start:]
                
                # フィールド定義を抽出
                field_pattern = r'^\s+(\w+)\s*=\s*(?:fields\.|ma\.)'
                fields = set()
                for line in class_body.split('\n'):
                    field_match = re.match(field_pattern, line)
                    if field_match:
                        fields.add(field_match.group(1))
                
                # SQLAlchemyAutoSchemaの場合、Metaクラスからモデルを取得
                if 'SQLAlchemyAutoSchema' in content or 'SQLAlchemySchema' in content:
                    meta_pattern = r'class\s+Meta\s*:.*?model\s*=\s*(\w+)'
                    meta_match = re.search(meta_pattern, class_body, re.DOTALL)
                    if meta_match:
                        model_name = meta_match.group(1)
                        # モデルのフィールドも追加（簡易的に）
                        fields.add('id')  # 通常idは存在する
                
                if fields:
                    schema_fields[schema_name] = fields
        
        # 3. 整合性チェック
        all_backend_fields = set()
        for fields in schema_fields.values():
            all_backend_fields.update(fields)
        
        # 一般的なフィールド名（誤検出を避けるため除外）
        common_fields = {'id', 'name', 'value', 'data', 'type', 'status', 'error', 'message', 
                        'success', 'result', 'items', 'count', 'total', 'page', 'limit',
                        'x', 'y', 'width', 'height', 'key', 'index', 'length', 'size'}
        
        # フロントエンドで参照されているがバックエンドに存在しないフィールド
        for field in frontend_fields:
            if field in common_fields:
                continue
            if field not in all_backend_fields and len(field) > 2:
                # スキーマで定義されていないフィールドへの参照
                # ただし、これは必ずしもエラーではない（ローカル変数の可能性）
                # 重要なフィールド（_id, _at で終わるもの）のみチェック
                if field.endswith('_id') or field.endswith('_at') or field.endswith('_name'):
                    issues.append({
                        'type': 'frontend_references_missing_field',
                        'field': field,
                        'severity': 'warning',
                        'details': f"Frontend references '{field}' but no matching schema field found"
                    })
        
        if issues:
            logger.debug(f"[G-23] Found {len(issues)} API contract consistency issues")
        else:
            logger.debug("[G-23] API contract consistency check passed")
        
        return issues
    
    def _auto_fix_api_contract_consistency(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        API契約整合性の問題を報告（自動修正は危険なため行わない）
        
        このチェックは主に警告として機能し、開発者に問題を通知する
        """
        if not issues:
            return generated_files
        
        # 自動修正は行わず、警告のみ
        for issue in issues:
            logger.debug(f"[G-23] ⚠ {issue['details']}")
        
        return generated_files

    # ============================================
    # G-24: back_populates相互参照整合性チェック
    # ============================================
    
    def _validate_back_populates_consistency(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemy relationship の back_populates 相互参照整合性を検証
        
        back_populates を使用する場合、双方のモデルに対応する relationship が必要
        片方にしか定義がない場合、SQLAlchemy が InvalidRequestError を発生させる
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # 1. 全モデルの relationship と back_populates を収集
        relationships = {}  # {(model_name, attr_name): (target_model, back_populates_value, filepath, line)}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            # 現在のクラス名を追跡
            current_class = None
            
            for line_num, line in enumerate(lines, 1):
                # クラス定義を検出
                class_match = re.match(r'^class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                    continue
                
                if not current_class:
                    continue
                
                # relationship定義を検出
                # パターン1: attr: Mapped[...] = relationship(..., back_populates="...")
                # パターン2: attr = relationship(..., back_populates="...")
                rel_patterns = [
                    r'(\w+)\s*:\s*Mapped\[.*?\]\s*=\s*relationship\s*\(\s*["\'](\w+)["\'].*?back_populates\s*=\s*["\'](\w+)["\']',
                    r'(\w+)\s*=\s*relationship\s*\(\s*["\'](\w+)["\'].*?back_populates\s*=\s*["\'](\w+)["\']',
                    r'(\w+)\s*:\s*Mapped\[.*?\]\s*=\s*relationship\s*\(\s*(\w+)\s*,.*?back_populates\s*=\s*["\'](\w+)["\']',
                ]
                
                for pattern in rel_patterns:
                    match = re.search(pattern, line)
                    if match:
                        attr_name = match.group(1)
                        target_model = match.group(2)
                        back_populates = match.group(3)
                        
                        relationships[(current_class, attr_name)] = (
                            target_model,
                            back_populates,
                            filepath,
                            line_num
                        )
                        break
        
        # 2. 相互参照を検証
        for (model_name, attr_name), (target_model, back_populates, filepath, line_num) in relationships.items():
            # target_model に back_populates で指定された属性が存在するか確認
            expected_key = (target_model, back_populates)
            
            if expected_key not in relationships:
                # 対応する relationship が存在しない
                issues.append({
                    'type': 'missing_back_populates_target',
                    'model': model_name,
                    'attribute': attr_name,
                    'target_model': target_model,
                    'expected_attribute': back_populates,
                    'file': filepath,
                    'line': line_num,
                    'severity': 'error',
                    'details': f"'{model_name}.{attr_name}' has back_populates='{back_populates}', but '{target_model}' has no '{back_populates}' attribute"
                })
            else:
                # 存在する場合、相互参照が正しいか確認
                target_info = relationships[expected_key]
                target_back_populates = target_info[1]
                
                if target_back_populates != attr_name:
                    issues.append({
                        'type': 'back_populates_mismatch',
                        'model': model_name,
                        'attribute': attr_name,
                        'target_model': target_model,
                        'expected_attribute': back_populates,
                        'actual_back_populates': target_back_populates,
                        'file': filepath,
                        'line': line_num,
                        'severity': 'error',
                        'details': f"'{model_name}.{attr_name}' expects '{target_model}.{back_populates}' to back_populates='{attr_name}', but it back_populates='{target_back_populates}'"
                    })
        
        if issues:
            logger.debug(f"[G-24] Found {len(issues)} back_populates consistency issues")
        else:
            logger.debug("[G-24] All back_populates references are consistent")
        
        return issues
    
    def _auto_fix_back_populates_consistency(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        back_populates整合性の問題を自動修正
        
        欠落しているrelationship定義を追加する
        """
        import re
        
        if not issues or not self.llm_manager:
            return generated_files
        
        # ファイルごとにグループ化
        files_to_fix = {}
        for issue in issues:
            if issue['type'] != 'missing_back_populates_target':
                continue
            
            target_model = issue['target_model']
            
            # target_modelが定義されているファイルを探す
            for filepath, content in generated_files.items():
                if not filepath.endswith('.py'):
                    continue
                if f"class {target_model}" in content:
                    if filepath not in files_to_fix:
                        files_to_fix[filepath] = []
                    files_to_fix[filepath].append(issue)
                    break
        
        for filepath, file_issues in files_to_fix.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            fixes_info = []
            for issue in file_issues:
                fixes_info.append(
                    f"- Add '{issue['expected_attribute']}' relationship to '{issue['target_model']}' "
                    f"with back_populates='{issue['attribute']}' targeting '{issue['model']}'"
                )
            
            prompt = f"""Fix missing back_populates relationship definitions in this Python file.

**File: {filepath}**
**Issues:**
{chr(10).join(fixes_info)}

**Current Code:**
```python
{original_code}
```

**Instructions:**
1. Add the missing relationship() definitions to the target models
2. Ensure back_populates values match on both sides
3. Use proper Mapped[] type annotations for SQLAlchemy 2.0
4. Add necessary imports (relationship, Mapped, List) if not present
5. Use TYPE_CHECKING for forward references if needed

Return ONLY the fixed Python code wrapped in ```python``` blocks.
"""
            
            try:
                logger.debug(f"[G-24] Fixing: {filepath}")
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a SQLAlchemy ORM expert. Add missing relationship definitions with correct back_populates.",
                    max_tokens=8000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                elif isinstance(response, dict) and 'content' in response:
                    response_text = response['content']
                else:
                    response_text = str(response)
                
                # コードブロックを抽出
                code_match = re.search(r'```python\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    
                    # 基本的な検証
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-24] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-24] ⚠ Fixed code too short, skipping")
                else:
                    logger.debug(f"[G-24] ⚠ No code block in response")
                    
            except Exception as e:
                logger.debug(f"[G-24] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-25: ForeignKey型注釈欠如チェック
    # ============================================
    
    def _validate_foreignkey_type_annotation(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        SQLAlchemy 2.0 で ForeignKey に型注釈がない場合を検出
        
        SQLAlchemy 2.0 では Mapped[] 型注釈が推奨される
        型注釈がないと型推論が効かず、IDE補完やmypyチェックが機能しない
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            current_class = None
            
            for line_num, line in enumerate(lines, 1):
                # クラス定義を検出
                class_match = re.match(r'^class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                    continue
                
                if not current_class:
                    continue
                
                # ForeignKeyを使用しているが型注釈がないパターンを検出
                # ❌ user_id = Column(ForeignKey("user.id"))
                # ❌ user_id = mapped_column(ForeignKey("user.id"))
                # ✅ user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
                
                # 型注釈なしのForeignKey
                bad_patterns = [
                    r'^\s+(\w+)\s*=\s*(?:Column|mapped_column)\s*\(\s*(?:Integer,\s*)?ForeignKey',
                    r'^\s+(\w+)\s*=\s*(?:Column|mapped_column)\s*\(\s*ForeignKey',
                ]
                
                for pattern in bad_patterns:
                    match = re.search(pattern, line)
                    if match:
                        attr_name = match.group(1)
                        
                        # 同じ行に型注釈があるかチェック
                        if not re.search(rf'{attr_name}\s*:\s*Mapped\[', line):
                            issues.append({
                                'type': 'missing_foreignkey_type_annotation',
                                'class': current_class,
                                'attribute': attr_name,
                                'file': filepath,
                                'line': line_num,
                                'severity': 'warning',
                                'details': f"'{current_class}.{attr_name}' uses ForeignKey without Mapped[] type annotation"
                            })
                        break
        
        if issues:
            logger.debug(f"[G-25] Found {len(issues)} ForeignKey type annotation issues")
        else:
            logger.debug("[G-25] All ForeignKey columns have proper type annotations")
        
        return issues
    
    def _auto_fix_foreignkey_type_annotation(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        ForeignKey型注釈欠如を自動修正
        """
        import re
        
        if not issues:
            return generated_files
        
        for issue in issues:
            filepath = issue['file']
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            attr_name = issue['attribute']
            
            # パターン: attr = mapped_column(ForeignKey(...)) → attr: Mapped[int] = mapped_column(ForeignKey(...))
            pattern = rf'^(\s+){attr_name}\s*=\s*(mapped_column\s*\(\s*ForeignKey)'
            replacement = rf'\1{attr_name}: Mapped[int] = \2'
            
            new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
            
            # Columnパターンも対応
            pattern2 = rf'^(\s+){attr_name}\s*=\s*(Column\s*\(\s*(?:Integer,\s*)?ForeignKey)'
            replacement2 = rf'\1{attr_name}: Mapped[int] = mapped_column(\2'
            
            new_content = re.sub(pattern2, replacement2, new_content, flags=re.MULTILINE)
            
            if new_content != content:
                generated_files[filepath] = new_content
                logger.debug(f"[G-25] ✅ Fixed {attr_name} in {filepath}")
        
        return generated_files

    # ============================================
    # G-26: Mapped型とSQLAlchemy型の不一致チェック
    # ============================================
    
    def _validate_mapped_sqlalchemy_type_mismatch(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Mapped[]型注釈とmapped_column()のSQLAlchemy型の不一致を検出
        
        例: Mapped[int] = mapped_column(String) ← 型不一致
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # Python型とSQLAlchemy型のマッピング
        type_mapping = {
            'int': ['Integer', 'BigInteger', 'SmallInteger'],
            'str': ['String', 'Text', 'VARCHAR', 'CHAR'],
            'float': ['Float', 'Numeric', 'DECIMAL'],
            'bool': ['Boolean'],
            'datetime': ['DateTime'],
            'date': ['Date'],
            'time': ['Time'],
            'bytes': ['LargeBinary', 'BLOB'],
        }
        
        # 逆マッピング
        sqlalchemy_to_python = {}
        for py_type, sa_types in type_mapping.items():
            for sa_type in sa_types:
                sqlalchemy_to_python[sa_type.lower()] = py_type
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            current_class = None
            
            for line_num, line in enumerate(lines, 1):
                class_match = re.match(r'^class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                    continue
                
                if not current_class:
                    continue
                
                # パターン: attr: Mapped[PythonType] = mapped_column(SQLAlchemyType, ...)
                match = re.search(
                    r'(\w+)\s*:\s*Mapped\[(\w+)\]\s*=\s*mapped_column\s*\(\s*(\w+)',
                    line
                )
                
                if match:
                    attr_name = match.group(1)
                    python_type = match.group(2).lower()
                    sqlalchemy_type = match.group(3).lower()
                    
                    # ForeignKeyやprimary_keyは特別扱い
                    if sqlalchemy_type in ['foreignkey', 'primary_key']:
                        continue
                    
                    # 型チェック
                    expected_python = sqlalchemy_to_python.get(sqlalchemy_type)
                    if expected_python and expected_python != python_type:
                        # Optional[]やList[]などの複合型は無視
                        if python_type not in ['optional', 'list', 'dict', 'any']:
                            issues.append({
                                'type': 'mapped_type_mismatch',
                                'class': current_class,
                                'attribute': attr_name,
                                'python_type': python_type,
                                'sqlalchemy_type': sqlalchemy_type,
                                'expected_python_type': expected_python,
                                'file': filepath,
                                'line': line_num,
                                'severity': 'warning',
                                'details': f"'{current_class}.{attr_name}': Mapped[{python_type}] with {sqlalchemy_type} (expected Mapped[{expected_python}])"
                            })
        
        if issues:
            logger.debug(f"[G-26] Found {len(issues)} Mapped/SQLAlchemy type mismatch issues")
        else:
            logger.debug("[G-26] All Mapped types match SQLAlchemy types")
        
        return issues
    
    def _auto_fix_mapped_sqlalchemy_type_mismatch(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Mapped型とSQLAlchemy型の不一致を自動修正
        """
        import re
        
        if not issues:
            return generated_files
        
        for issue in issues:
            filepath = issue['file']
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            attr_name = issue['attribute']
            old_type = issue['python_type']
            new_type = issue['expected_python_type']
            
            # Mapped[old_type] → Mapped[new_type]
            pattern = rf'({attr_name}\s*:\s*Mapped\[){old_type}(\])'
            replacement = rf'\g<1>{new_type}\g<2>'
            
            new_content = re.sub(pattern, replacement, content, flags=re.IGNORECASE)
            
            if new_content != content:
                generated_files[filepath] = new_content
                logger.debug(f"[G-26] ✅ Fixed {attr_name} type: {old_type} → {new_type}")
        
        return generated_files

    # ============================================
    # G-27: relationship overlaps検出
    # ============================================
    
    def _validate_relationship_overlaps(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        同じカラムを複数のrelationshipが参照する場合を検出
        
        SQLAlchemyは警告を出すが、実行時エラーにはならない
        しかし、意図しない動作を引き起こす可能性がある
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # モデルごとのrelationshipを収集
        # {model_name: [(attr_name, target_model, has_back_populates, filepath, line)]}
        model_relationships = {}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            current_class = None
            
            for line_num, line in enumerate(lines, 1):
                class_match = re.match(r'^class\s+(\w+)\s*\(', line)
                if class_match:
                    current_class = class_match.group(1)
                    if current_class not in model_relationships:
                        model_relationships[current_class] = []
                    continue
                
                if not current_class:
                    continue
                
                # relationship定義を検出
                rel_match = re.search(
                    r'(\w+)\s*(?::\s*Mapped\[.*?\])?\s*=\s*relationship\s*\(\s*["\']?(\w+)["\']?',
                    line
                )
                
                if rel_match:
                    attr_name = rel_match.group(1)
                    target_model = rel_match.group(2)
                    has_back_populates = 'back_populates' in line
                    
                    model_relationships[current_class].append({
                        'attr_name': attr_name,
                        'target_model': target_model,
                        'has_back_populates': has_back_populates,
                        'filepath': filepath,
                        'line': line_num
                    })
        
        # 同じターゲットへの複数relationshipをチェック
        for model_name, relationships in model_relationships.items():
            target_counts = {}
            for rel in relationships:
                target = rel['target_model']
                if target not in target_counts:
                    target_counts[target] = []
                target_counts[target].append(rel)
            
            for target, rels in target_counts.items():
                if len(rels) > 1:
                    # 複数のrelationshipが同じターゲットを参照
                    # back_populatesがない場合は警告
                    missing_back_populates = [r for r in rels if not r['has_back_populates']]
                    
                    if missing_back_populates:
                        for rel in missing_back_populates:
                            issues.append({
                                'type': 'relationship_overlap',
                                'model': model_name,
                                'attribute': rel['attr_name'],
                                'target_model': target,
                                'file': rel['filepath'],
                                'line': rel['line'],
                                'severity': 'warning',
                                'details': f"'{model_name}' has multiple relationships to '{target}' without back_populates"
                            })
        
        if issues:
            logger.debug(f"[G-27] Found {len(issues)} relationship overlap issues")
        else:
            logger.debug("[G-27] No relationship overlap issues found")
        
        return issues
    
    def _auto_fix_relationship_overlaps(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        relationship overlapsの問題は警告のみ（自動修正は複雑なため）
        """
        if issues:
            for issue in issues:
                logger.debug(f"[G-27] ⚠ {issue['details']}")
        
        return generated_files

    # ============================================
    # G-28: 循環インポート検出
    # ============================================
    
    def _validate_circular_imports(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Python/Flaskの循環インポートを検出
        
        循環インポートはImportErrorやAttributeErrorを引き起こす
        特にFlaskアプリケーションで頻発する問題
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        from pathlib import Path
        
        issues = []
        
        # インポートグラフを構築
        # {module_name: [imported_modules]}
        import_graph = {}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # モジュール名を抽出（ファイルパスから）
            module_name = Path(filepath).stem
            if module_name == '__init__':
                module_name = Path(filepath).parent.name
            
            content = content.replace('\r\n', '\n')
            
            imports = set()
            
            # from X import Y パターン
            for match in re.finditer(r'^from\s+([\w.]+)\s+import', content, re.MULTILINE):
                imported = match.group(1).split('.')[0]  # トップレベルモジュール
                imports.add(imported)
            
            # import X パターン
            for match in re.finditer(r'^import\s+([\w.]+)', content, re.MULTILINE):
                imported = match.group(1).split('.')[0]
                imports.add(imported)
            
            import_graph[module_name] = list(imports)
        
        # 循環を検出（DFS）
        def find_cycle(node, visited, path):
            if node in path:
                cycle_start = path.index(node)
                return path[cycle_start:] + [node]
            
            if node in visited:
                return None
            
            visited.add(node)
            path.append(node)
            
            for neighbor in import_graph.get(node, []):
                if neighbor in import_graph:  # プロジェクト内のモジュールのみ
                    cycle = find_cycle(neighbor, visited, path)
                    if cycle:
                        return cycle
            
            path.pop()
            return None
        
        visited = set()
        for module in import_graph:
            if module not in visited:
                cycle = find_cycle(module, visited, [])
                if cycle:
                    cycle_str = ' -> '.join(cycle)
                    issues.append({
                        'type': 'circular_import',
                        'cycle': cycle,
                        'severity': 'error',
                        'details': f"Circular import detected: {cycle_str}"
                    })
        
        if issues:
            logger.debug(f"[G-28] Found {len(issues)} circular import issues")
        else:
            logger.debug("[G-28] No circular imports detected")
        
        return issues
    
    def _auto_fix_circular_imports(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        循環インポートの問題は警告のみ（自動修正は複雑なため）
        
        推奨解決策をログに出力
        """
        if issues:
            for issue in issues:
                logger.debug(f"[G-28] ⚠ {issue['details']}")
                logger.debug("[G-28] 推奨解決策:")
                logger.debug("  1. TYPE_CHECKING ブロックを使用")
                logger.debug("  2. 遅延インポート（関数内import）")
                logger.debug("  3. 共通モジュールにdbインスタンスを分離")
        
        return generated_files

    # ============================================
    # G-29: Marshmallow Nestedスキーマ循環参照検出
    # ============================================
    
    def _validate_marshmallow_nested_circular(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Marshmallow Nestedスキーマの循環参照を検出
        
        スキーマAがスキーマBをNested()で参照し、
        スキーマBがスキーマAをNested()で参照する場合を検出
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # スキーマのNested参照を収集
        # {schema_name: [nested_schema_names]}
        schema_graph = {}
        schema_files = {}  # {schema_name: (filepath, line)}
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            if 'schema' not in filepath.lower() and 'Schema' not in content:
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            current_schema = None
            
            for line_num, line in enumerate(lines, 1):
                # スキーマクラス定義を検出
                class_match = re.match(r'^class\s+(\w+Schema)\s*\(', line)
                if class_match:
                    current_schema = class_match.group(1)
                    if current_schema not in schema_graph:
                        schema_graph[current_schema] = []
                        schema_files[current_schema] = (filepath, line_num)
                    continue
                
                if not current_schema:
                    continue
                
                # Nested()フィールドを検出
                nested_match = re.search(r'fields\.Nested\s*\(\s*["\']?(\w+)["\']?', line)
                if nested_match:
                    nested_schema = nested_match.group(1)
                    schema_graph[current_schema].append(nested_schema)
        
        # 循環を検出
        def find_schema_cycle(schema, visited, path):
            if schema in path:
                cycle_start = path.index(schema)
                return path[cycle_start:] + [schema]
            
            if schema in visited:
                return None
            
            visited.add(schema)
            path.append(schema)
            
            for nested in schema_graph.get(schema, []):
                if nested in schema_graph:
                    cycle = find_schema_cycle(nested, visited, path)
                    if cycle:
                        return cycle
            
            path.pop()
            return None
        
        visited = set()
        for schema in schema_graph:
            if schema not in visited:
                cycle = find_schema_cycle(schema, visited, [])
                if cycle:
                    cycle_str = ' -> '.join(cycle)
                    filepath, line = schema_files.get(cycle[0], ('unknown', 0))
                    issues.append({
                        'type': 'marshmallow_nested_circular',
                        'cycle': cycle,
                        'file': filepath,
                        'line': line,
                        'severity': 'warning',
                        'details': f"Marshmallow Nested circular reference: {cycle_str}"
                    })
        
        if issues:
            logger.debug(f"[G-29] Found {len(issues)} Marshmallow Nested circular issues")
        else:
            logger.debug("[G-29] No Marshmallow Nested circular references detected")
        
        return issues
    
    def _auto_fix_marshmallow_nested_circular(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Marshmallow Nested循環参照は警告のみ
        
        推奨解決策をログに出力
        """
        if issues:
            for issue in issues:
                logger.debug(f"[G-29] ⚠ {issue['details']}")
                logger.debug("[G-29] 推奨解決策:")
                logger.debug("  1. 文字列参照を使用: fields.Nested('SchemaName')")
                logger.debug("  2. exclude パラメータで循環を断ち切る")
                logger.debug("  3. only パラメータで必要なフィールドのみ含める")
        
        return generated_files

    # ============================================
    # G-31: Marshmallow API誤用検出
    # ============================================
    
    def _validate_marshmallow_api_usage(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        Marshmallow固有のAPI誤用を静的に検出
        
        検出項目:
        - required=True と load_default の同時使用（ValueError）
        - required=True と missing の同時使用（deprecated、ValueError）
        - dump_only=True と required=True の同時使用（論理矛盾）
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        for filepath, content in generated_files.items():
            if not filepath.endswith('.py'):
                continue
            
            # スキーマファイルまたはMarshmallowを使用しているファイルのみ対象
            if 'schema' not in filepath.lower() and 'Schema' not in content:
                continue
            if 'marshmallow' not in content and 'fields.' not in content:
                continue
            
            content = content.replace('\r\n', '\n')
            
            # 全てのフィールド定義開始位置を見つける
            # パターン: field_name = fields.Type(
            field_starts = list(re.finditer(r'(\w+)\s*=\s*fields\.(\w+)\s*\(', content))
            
            for match in field_starts:
                field_name = match.group(1)
                field_type = match.group(2)
                start_pos = match.end() - 1  # '(' の位置
                
                # 括弧のネストを考慮して閉じ括弧を見つける
                paren_count = 1
                pos = start_pos + 1
                while pos < len(content) and paren_count > 0:
                    if content[pos] == '(':
                        paren_count += 1
                    elif content[pos] == ')':
                        paren_count -= 1
                    pos += 1
                
                # フィールド定義全体を抽出
                field_def = content[start_pos:pos]
                
                # 行番号を計算
                line_num = content[:match.start()].count('\n') + 1
                
                # ★ 文字列リテラルを除外してから検出（誤検出防止）
                # 文字列内の "required=True" 等を誤検出しないため
                field_def_no_strings = re.sub(r'""".*?"""', '', field_def, flags=re.DOTALL)
                field_def_no_strings = re.sub(r"'''.*?'''", '', field_def_no_strings, flags=re.DOTALL)
                field_def_no_strings = re.sub(r'"[^"\\]*(?:\\.[^"\\]*)*"', '', field_def_no_strings)
                field_def_no_strings = re.sub(r"'[^'\\]*(?:\\.[^'\\]*)*'", '', field_def_no_strings)
                
                # パターン1: required=True と load_default の同時使用
                has_required_true = re.search(r'required\s*=\s*True', field_def_no_strings)
                has_load_default = re.search(r'load_default\s*=', field_def_no_strings)
                
                if has_required_true and has_load_default:
                    issues.append({
                        'type': 'marshmallow_required_load_default',
                        'file': filepath,
                        'line': line_num,
                        'field': field_name,
                        'details': f"Marshmallow 3.x: 'required=True' and 'load_default' cannot be used together in field '{field_name}'. This will raise ValueError at import time.",
                        'fix': "Remove 'required=True' (use required=False with load_default) or remove 'load_default' (keep required=True for mandatory fields)"
                    })
                    logger.debug(f"[G-31] {filepath}:{line_num} - required=True + load_default in '{field_name}'")
                
                # パターン2: required=True と missing の同時使用（deprecated）
                has_missing = re.search(r'missing\s*=', field_def_no_strings)
                
                if has_required_true and has_missing:
                    issues.append({
                        'type': 'marshmallow_required_missing',
                        'file': filepath,
                        'line': line_num,
                        'field': field_name,
                        'details': f"Marshmallow 3.x: 'required=True' and 'missing' cannot be used together in field '{field_name}'. 'missing' is deprecated, use 'load_default' instead.",
                        'fix': "Remove 'required=True' and replace 'missing' with 'load_default'"
                    })
                    logger.debug(f"[G-31] {filepath}:{line_num} - required=True + missing in '{field_name}'")
                
                # パターン3: dump_only=True と required=True の同時使用（論理矛盾）
                has_dump_only = re.search(r'dump_only\s*=\s*True', field_def_no_strings)
                
                if has_dump_only and has_required_true:
                    issues.append({
                        'type': 'marshmallow_dump_only_required',
                        'file': filepath,
                        'line': line_num,
                        'field': field_name,
                        'details': f"Marshmallow: 'dump_only=True' and 'required=True' is a logical contradiction in field '{field_name}'. dump_only fields are ignored during load, so required has no effect.",
                        'fix': "Remove 'required=True' from dump_only field"
                    })
                    logger.debug(f"[G-31] {filepath}:{line_num} - dump_only=True + required=True in '{field_name}'")
        
        if issues:
            logger.debug(f"[G-31] Found {len(issues)} Marshmallow API usage issues")
        else:
            logger.debug("[G-31] No Marshmallow API usage issues detected")
        
        return issues
    
    def _auto_fix_marshmallow_api_usage(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        Marshmallow API誤用をLLMで修正
        
        Args:
            issues: 検出された問題のリスト
            generated_files: 生成されたファイル
        
        Returns:
            更新されたファイル辞書
        """
        if not issues:
            return generated_files
        
        # ファイルごとにグループ化
        issues_by_file = {}
        for issue in issues:
            filepath = issue['file']
            if filepath not in issues_by_file:
                issues_by_file[filepath] = []
            issues_by_file[filepath].append(issue)
        
        for filepath, file_issues in issues_by_file.items():
            if filepath not in generated_files:
                continue
            
            code = generated_files[filepath]
            
            # 問題の詳細
            issue_descriptions = []
            for issue in file_issues:
                desc = f"- Line {issue['line']}: Field '{issue['field']}' - {issue['details']}"
                desc += f"\n  Suggested fix: {issue['fix']}"
                issue_descriptions.append(desc)
            
            issues_text = "\n".join(issue_descriptions)
            
            fix_prompt = f"""Fix the Marshmallow schema API usage errors.

## File: {filepath}
```python
{code}
```

## Issues Found:
{issues_text}

## Fix Instructions:
1. For 'required=True' + 'load_default' conflict:
   - If the field should have a default value when not provided: Remove 'required=True', keep 'load_default'
   - If the field is truly mandatory: Remove 'load_default', keep 'required=True'
   - Usually, if there's a sensible default (like 'Anonymous' for player_name), use required=False with load_default

2. For 'required=True' + 'missing' conflict:
   - Replace 'missing' with 'load_default' (missing is deprecated in Marshmallow 3.x)
   - Remove 'required=True'

3. For 'dump_only=True' + 'required=True' conflict:
   - Remove 'required=True' (dump_only fields are only for serialization, not deserialization)

## Output
Return ONLY the complete fixed Python code without markdown code blocks.
"""
            
            try:
                logger.debug(f"[G-31] Fixing: {filepath}")
                
                response = self._call_llm_api(fix_prompt)
                
                if response:
                    import re
                    fixed_code = response.strip()
                    fixed_code = re.sub(r'^```\w*\n?', '', fixed_code)
                    fixed_code = re.sub(r'\n?```$', '', fixed_code)
                    
                    if fixed_code and len(fixed_code) > 100:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[G-31] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[G-31] ⚠ Generated code too short for {filepath}")
                else:
                    logger.debug(f"[G-31] ⚠ No response for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[G-31] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # G-30: JavaScript this参照喪失チェック
    # ============================================
    
    def _validate_js_this_context_loss(
        self,
        generated_files: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """
        JavaScriptでthis参照が喪失するパターンを検出
        
        コールバック関数でthis.methodを直接渡すと、
        thisコンテキストが喪失してundefinedになる
        
        検出パターン:
        - requestAnimationFrame(this.method)
        - setTimeout(this.method, ...)
        - setInterval(this.method, ...)
        - addEventListener(..., this.method)
        - .then(this.method)
        - .catch(this.method)
        - .forEach(this.method)
        - .map(this.method)
        - .filter(this.method)
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            検出された問題のリスト
        """
        import re
        
        issues = []
        
        # this喪失を引き起こすパターン
        dangerous_patterns = [
            (r'requestAnimationFrame\s*\(\s*this\.(\w+)\s*\)', 'requestAnimationFrame'),
            (r'setTimeout\s*\(\s*this\.(\w+)\s*,', 'setTimeout'),
            (r'setInterval\s*\(\s*this\.(\w+)\s*,', 'setInterval'),
            (r'addEventListener\s*\([^,]+,\s*this\.(\w+)\s*\)', 'addEventListener'),
            (r'\.then\s*\(\s*this\.(\w+)\s*\)', '.then()'),
            (r'\.catch\s*\(\s*this\.(\w+)\s*\)', '.catch()'),
            (r'\.forEach\s*\(\s*this\.(\w+)\s*\)', '.forEach()'),
            (r'\.map\s*\(\s*this\.(\w+)\s*\)', '.map()'),
            (r'\.filter\s*\(\s*this\.(\w+)\s*\)', '.filter()'),
        ]
        
        for filepath, content in generated_files.items():
            if not filepath.endswith(('.js', '.jsx', '.ts', '.tsx')):
                continue
            
            content = content.replace('\r\n', '\n')
            lines = content.split('\n')
            
            for line_num, line in enumerate(lines, 1):
                for pattern, context in dangerous_patterns:
                    match = re.search(pattern, line)
                    if match:
                        method_name = match.group(1)
                        
                        # アロー関数やbind()が使われていないかチェック
                        # 正しいパターン: requestAnimationFrame(() => this.method())
                        # 正しいパターン: requestAnimationFrame(this.method.bind(this))
                        if '=>' not in line and '.bind(' not in line:
                            issues.append({
                                'type': 'this_context_loss',
                                'method': method_name,
                                'context': context,
                                'file': filepath,
                                'line': line_num,
                                'severity': 'error',
                                'details': f"'{context}' with 'this.{method_name}' will lose 'this' context"
                            })
        
        if issues:
            logger.debug(f"[G-30] Found {len(issues)} JavaScript this context loss issues")
        else:
            logger.debug("[G-30] No JavaScript this context loss issues found")
        
        return issues
    
    def _auto_fix_js_this_context_loss(
        self,
        issues: List[Dict[str, Any]],
        generated_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        JavaScript this参照喪失を自動修正
        
        this.method → () => this.method() または this.method.bind(this)
        """
        import re
        
        if not issues:
            return generated_files
        
        for issue in issues:
            filepath = issue['file']
            if filepath not in generated_files:
                continue
            
            content = generated_files[filepath]
            method_name = issue['method']
            context = issue['context']
            
            # パターンごとに修正
            if context == 'requestAnimationFrame':
                # requestAnimationFrame(this.method) → requestAnimationFrame((t) => this.method(t))
                pattern = rf'requestAnimationFrame\s*\(\s*this\.{method_name}\s*\)'
                replacement = f'requestAnimationFrame((t) => this.{method_name}(t))'
                content = re.sub(pattern, replacement, content)
            
            elif context in ['setTimeout', 'setInterval']:
                # setTimeout(this.method, 100) → setTimeout(() => this.method(), 100)
                pattern = rf'{context}\s*\(\s*this\.{method_name}\s*,'
                replacement = f'{context}(() => this.{method_name}(),'
                content = re.sub(pattern, replacement, content)
            
            elif context == 'addEventListener':
                # addEventListener('click', this.method) → addEventListener('click', (e) => this.method(e))
                pattern = rf"addEventListener\s*\(\s*(['\"][^'\"]+['\"])\s*,\s*this\.{method_name}\s*\)"
                replacement = rf"addEventListener(\1, (e) => this.{method_name}(e))"
                content = re.sub(pattern, replacement, content)
            
            elif context in ['.then()', '.catch()']:
                # .then(this.method) → .then((x) => this.method(x))
                dot_method = context.replace('()', '')
                pattern = rf'{re.escape(dot_method)}\s*\(\s*this\.{method_name}\s*\)'
                replacement = f'{dot_method}((x) => this.{method_name}(x))'
                content = re.sub(pattern, replacement, content)
            
            elif context in ['.forEach()', '.map()', '.filter()']:
                # .forEach(this.method) → .forEach((item) => this.method(item))
                dot_method = context.replace('()', '')
                pattern = rf'{re.escape(dot_method)}\s*\(\s*this\.{method_name}\s*\)'
                replacement = f'{dot_method}((item) => this.{method_name}(item))'
                content = re.sub(pattern, replacement, content)
            
            generated_files[filepath] = content
            logger.debug(f"[G-30] ✅ Fixed this.{method_name} in {context}")
        
        return generated_files

    # ============================================
    # ESLint統合: JavaScript品質チェック
    # ============================================
    
    def _check_eslint_available(self) -> bool:
        """
        ESLintが利用可能かチェック
        
        Returns:
            ESLintが利用可能な場合True
        """
        import subprocess
        import shutil
        import platform
        
        is_windows = platform.system() == 'Windows'
        
        # npxが利用可能かチェック
        # Windows: npx.cmd, Unix: npx
        npx_cmd = shutil.which('npx.cmd') if is_windows else shutil.which('npx')
        if not npx_cmd and is_windows:
            npx_cmd = shutil.which('npx')  # フォールバック
        
        if npx_cmd:
            try:
                result = subprocess.run(
                    [npx_cmd, 'eslint', '--version'],
                    capture_output=True,
                    timeout=30,
                    shell=is_windows,  # Windowsではshell=Trueが必要な場合がある
                    encoding='utf-8',
                    errors='replace'
                )
                if result.returncode == 0:
                    version = result.stdout.strip()
                    logger.debug(f"[ESLint] Available via npx: {version}")
                    self._eslint_cmd = [npx_cmd, 'eslint']
                    return True
            except Exception as e:
                logger.debug(f"[ESLint] npx check failed: {e}")
        
        # 直接eslintコマンドをチェック
        # Windows: eslint.cmd, Unix: eslint
        eslint_cmd = shutil.which('eslint.cmd') if is_windows else shutil.which('eslint')
        if not eslint_cmd and is_windows:
            eslint_cmd = shutil.which('eslint')  # フォールバック
        
        if eslint_cmd:
            try:
                result = subprocess.run(
                    [eslint_cmd, '--version'],
                    capture_output=True,
                    timeout=10,
                    shell=is_windows,
                    encoding='utf-8',
                    errors='replace'
                )
                if result.returncode == 0:
                    version = result.stdout.strip()
                    logger.debug(f"[ESLint] Available directly: {version}")
                    self._eslint_cmd = [eslint_cmd]
                    return True
            except Exception as e:
                logger.debug(f"[ESLint] direct check failed: {e}")
        
        logger.debug("[ESLint] Not available")
        return False
    
    def _run_eslint_on_files(
        self,
        generated_files: Dict[str, str]
    ) -> Dict[str, Any]:
        """
        生成されたJavaScriptファイルに対してESLintを実行
        
        Args:
            generated_files: 生成されたファイル {filepath: content}
        
        Returns:
            ESLint結果 {
                'has_errors': bool,
                'errors': List[Dict],
                'warnings': List[Dict],
                'files_checked': int
            }
        """
        import subprocess
        import tempfile
        import os
        import json
        
        result = {
            'has_errors': False,
            'errors': [],
            'warnings': [],
            'files_checked': 0
        }
        
        # JavaScriptファイルを抽出
        js_files = {
            fp: content for fp, content in generated_files.items()
            if fp.endswith(('.js', '.jsx', '.mjs'))
        }
        
        if not js_files:
            logger.debug("[ESLint] No JavaScript files to check")
            return result
        
        result['files_checked'] = len(js_files)
        
        # 一時ディレクトリにファイルを書き出してESLint実行
        with tempfile.TemporaryDirectory() as tmpdir:
            # ESLint v9 Flat Config形式（eslint.config.js）
            eslint_config_js = '''export default [
    {
        languageOptions: {
            ecmaVersion: 2021,
            sourceType: "module",
            globals: {
                // Browser globals
                window: "readonly",
                document: "readonly",
                console: "readonly",
                fetch: "readonly",
                requestAnimationFrame: "readonly",
                cancelAnimationFrame: "readonly",
                setTimeout: "readonly",
                setInterval: "readonly",
                clearTimeout: "readonly",
                clearInterval: "readonly",
                localStorage: "readonly",
                sessionStorage: "readonly",
                alert: "readonly",
                confirm: "readonly",
                prompt: "readonly",
                performance: "readonly",
                navigator: "readonly",
                location: "readonly",
                history: "readonly",
                Image: "readonly",
                Audio: "readonly",
                Event: "readonly",
                CustomEvent: "readonly",
                FormData: "readonly",
                URLSearchParams: "readonly",
                URL: "readonly",
                Blob: "readonly",
                FileReader: "readonly",
                WebSocket: "readonly",
                Worker: "readonly",
                HTMLElement: "readonly",
                HTMLCanvasElement: "readonly",
                CanvasRenderingContext2D: "readonly",
                KeyboardEvent: "readonly",
                MouseEvent: "readonly",
                Headers: "readonly",
                Request: "readonly",
                Response: "readonly"
            }
        },
        rules: {
            // Errors (critical issues)
            "no-undef": "error",
            "no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^_"}],
            "no-unreachable": "error",
            "no-dupe-keys": "error",
            "no-duplicate-case": "error",
            "no-empty": "error",
            "no-func-assign": "error",
            "no-invalid-regexp": "error",
            "no-obj-calls": "error",
            "no-sparse-arrays": "error",
            "use-isnan": "error",
            "valid-typeof": "error",
            // Warnings (quality improvements)
            "no-constant-condition": "warn",
            "no-debugger": "warn",
            "no-extra-semi": "warn",
            "no-redeclare": "warn"
        }
    }
];
'''
            
            config_path = os.path.join(tmpdir, 'eslint.config.js')
            with open(config_path, 'w', encoding='utf-8') as f:
                f.write(eslint_config_js)
            
            # JavaScriptファイルを一時ディレクトリに書き出し
            file_paths = []
            filepath_mapping = {}  # tmp_path -> original_path
            
            for filepath, content in js_files.items():
                # ディレクトリ構造を維持
                safe_path = filepath.replace('/', '_').replace('\\', '_')
                tmp_path = os.path.join(tmpdir, safe_path)
                
                with open(tmp_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                file_paths.append(tmp_path)
                filepath_mapping[tmp_path] = filepath
            
            # ESLint実行（v9 Flat Config）
            try:
                import platform
                is_windows = platform.system() == 'Windows'
                
                # _check_eslint_availableで設定されたコマンドを使用
                if hasattr(self, '_eslint_cmd') and self._eslint_cmd:
                    base_cmd = self._eslint_cmd
                else:
                    # フォールバック
                    base_cmd = ['npx', 'eslint']
                
                cmd = base_cmd + ['--format', 'json'] + file_paths
                
                proc_result = subprocess.run(
                    cmd,
                    capture_output=True,
                    timeout=60,
                    cwd=tmpdir,
                    shell=is_windows,
                    encoding='utf-8',
                    errors='replace'
                )
                
                # ESLintはエラーがあっても終了コード1を返す
                if proc_result.stdout:
                    try:
                        eslint_output = json.loads(proc_result.stdout)
                        
                        for file_result in eslint_output:
                            tmp_path = file_result.get('filePath', '')
                            original_path = filepath_mapping.get(tmp_path, tmp_path)
                            
                            for msg in file_result.get('messages', []):
                                issue = {
                                    'file': original_path,
                                    'line': msg.get('line', 0),
                                    'column': msg.get('column', 0),
                                    'rule': msg.get('ruleId', 'unknown'),
                                    'message': msg.get('message', ''),
                                    'severity': msg.get('severity', 1)  # 1=warning, 2=error
                                }
                                
                                if msg.get('severity', 1) == 2:
                                    result['errors'].append(issue)
                                else:
                                    result['warnings'].append(issue)
                        
                        result['has_errors'] = len(result['errors']) > 0
                        
                    except json.JSONDecodeError as e:
                        logger.debug(f"[ESLint] Failed to parse output: {e}")
                        logger.debug(f"[ESLint] stdout: {proc_result.stdout[:500]}")
                
                if proc_result.stderr and 'error' in proc_result.stderr.lower():
                    logger.debug(f"[ESLint] stderr: {proc_result.stderr[:500]}")
                    
            except subprocess.TimeoutExpired:
                logger.debug("[ESLint] Timeout expired")
            except Exception as e:
                logger.debug(f"[ESLint] Execution failed: {e}")
        
        logger.debug(f"[ESLint] Checked {result['files_checked']} files: {len(result['errors'])} errors, {len(result['warnings'])} warnings")
        
        return result
    
    def _fix_eslint_errors_with_llm(
        self,
        generated_files: Dict[str, str],
        eslint_errors: List[Dict[str, Any]]
    ) -> Dict[str, str]:
        """
        ESLintエラーをLLMで自動修正
        
        Args:
            generated_files: 生成されたファイル
            eslint_errors: ESLintエラーリスト
        
        Returns:
            修正後のファイル辞書
        """
        if not eslint_errors or not self.llm_manager:
            return generated_files
        
        import re
        
        # ファイルごとにエラーをグループ化
        errors_by_file = {}
        for error in eslint_errors:
            filepath = error['file']
            if filepath not in errors_by_file:
                errors_by_file[filepath] = []
            errors_by_file[filepath].append(error)
        
        for filepath, file_errors in errors_by_file.items():
            if filepath not in generated_files:
                continue
            
            original_code = generated_files[filepath]
            
            # エラー情報を整形
            error_details = []
            for err in file_errors:
                error_details.append(
                    f"- Line {err['line']}, Col {err['column']}: [{err['rule']}] {err['message']}"
                )
            
            prompt = f"""Fix the following ESLint errors in this JavaScript file.

**File: {filepath}**

**ESLint Errors:**
{chr(10).join(error_details)}

**Current Code:**
```javascript
{original_code}
```

**Instructions:**
1. Fix ALL the listed ESLint errors
2. For 'no-undef' errors: Either define the variable, import it, or add it as a parameter
3. For 'no-unused-vars' errors: Remove unused variables or use them appropriately
4. For other errors: Follow ESLint best practices to resolve them
5. Do NOT change the overall logic or functionality
6. Preserve all existing features and behavior

Return ONLY the fixed JavaScript code wrapped in ```javascript``` blocks.
"""
            
            try:
                logger.debug(f"[ESLint] Fixing {len(file_errors)} errors in {filepath}")
                
                response = self.llm_manager.generate_response(
                    prompt=prompt,
                    system_prompt="You are a JavaScript expert. Fix ESLint errors while preserving functionality.",
                    max_tokens=16000
                )
                
                if hasattr(response, 'content'):
                    response_text = response.content
                else:
                    response_text = str(response)
                
                # コード抽出
                code_match = re.search(r'```(?:javascript|js)?\s*(.*?)\s*```', response_text, re.DOTALL)
                if code_match:
                    fixed_code = code_match.group(1).strip()
                    if len(fixed_code) > len(original_code) * 0.5:
                        generated_files[filepath] = fixed_code
                        logger.debug(f"[ESLint] ✅ Fixed {filepath}")
                    else:
                        logger.debug(f"[ESLint] ⚠ Generated content too short for {filepath}")
                else:
                    logger.debug(f"[ESLint] ⚠ No code block found for {filepath}")
                    
            except Exception as e:
                logger.debug(f"[ESLint] ⚠ Error fixing {filepath}: {e}")
        
        return generated_files

    # ============================================
    # 総合レビュー（Comprehensive Review）
    # ============================================
    
    def _final_comprehensive_review(
        self,
        all_files: Dict[str, str],
        goal: str
    ) -> Dict[str, str]:
        """
        総合レビュー（16項目チェック＆自動修正）
        
        シングルステージ/マルチステージ共通で最後に必ず実行。
        「一発で動く」を保証するための最終チェック。
        
        Args:
            all_files: 生成されたファイル一式
            goal: 元のゴール（コンテキスト理解用）
        
        Returns:
            修正後のファイル一式
        """
        MAX_REVIEW_ATTEMPTS = 2  # 🔧 時間短縮: 4→2 (効果: 50%削減)
        
        logger.debug("[Comprehensive Review] Starting final comprehensive review...")
        
        previous_issue_count = float('inf')
        no_improvement_count = 0
        
        # 🆕 修正回数トラッカー（Whack-a-mole防止）
        attempt_tracker = self.FixAttemptTracker(max_attempts=2)
        
        # 🆕 D1: P4/P5で却下されたファイルを記録（以降のラウンドでスキップ）
        rejected_files = set()
        
        for attempt in range(MAX_REVIEW_ATTEMPTS):
            # ============================================================
            # 🆕 改修: 検出2レイヤ化
            # Layer 1: Blocking issues（ランタイムエラー、セキュリティ）のみ修正対象
            # Layer 2: Quality issues は最終報告のみ（自動修正しない）
            # ============================================================
            detection_result = self._detect_blocking_issues(all_files, goal)
            
            # エラー発生時は処理を中断（誤判定防止）
            if detection_result.get('error', False):
                error_msg = detection_result.get('error_message', 'Unknown error')
                logger.debug(f"[Blocking Detection] ⚠ Detection failed: {error_msg}")
                logger.debug(f"[Blocking Detection] Stopping review to prevent false success")
                break
            
            issues = detection_result.get('issues', [])
            current_issue_count = len(issues)
            
            if not issues:
                logger.debug(f"[Blocking Detection] ✅ No blocking issues found (attempt {attempt + 1})")
                break
            
            # ⭐ Zen HUD: 初期Review issue数を記録（最初のループのみ）
            if attempt == 0:
                self._zen_summary["review"]["initial"] = current_issue_count
                
                # ============================================
                # 差分スキャン: 初回検出結果をキャッシュに保存
                # ============================================
                if self._differential_scan_enabled:
                    self._issue_cache = self._cache_issues(issues, all_files)
                    logger.debug(f"[Differential Scan] Initial cache created with {current_issue_count} issue(s)")
            
            # 🆕 収束判定: issue数が減少していない場合
            if current_issue_count >= previous_issue_count:
                no_improvement_count += 1
                logger.debug(f"[Comprehensive Review] No improvement ({current_issue_count} >= {previous_issue_count}), count={no_improvement_count}")
                
                # 🔧 時間短縮: 1回でも改善なしなら即終了 (4→1)
                if no_improvement_count >= 1:
                    logger.debug(f"[Comprehensive Review] ⚠ No improvement detected, stopping early")
                    break
            else:
                no_improvement_count = 0
                improvement = previous_issue_count - current_issue_count
                if previous_issue_count != float('inf'):
                    logger.debug(f"[Comprehensive Review] Improved by {improvement} issue(s) ({previous_issue_count} -> {current_issue_count})")
            
            previous_issue_count = current_issue_count
            
            logger.debug(f"[Comprehensive Review] Found {current_issue_count} issue(s) (attempt {attempt + 1}/{MAX_REVIEW_ATTEMPTS})")
            
            # ユーザーへのフィードバック
            logger.debug(f"    {Icon.WARNING.value} Found {current_issue_count} issue(s) in comprehensive review")
            
            # 問題の概要を表示（最大5件）
            for i, issue in enumerate(issues[:5]):
                issue_file = issue.get('file', 'unknown')
                issue_type = issue.get('type', '?')
                issue_desc = issue.get('description', 'No description')[:60]
                logger.debug(f"      [{issue_type}] {issue_file}: {issue_desc}...")
            
            if len(issues) > 5:
                logger.debug(f"      ... and {len(issues) - 5} more issue(s)")
            
            # 2. 問題があれば修正
            logger.debug(f"    {Icon.GEAR.value} Attempting to fix issues...")
            fixed_files = self._fix_comprehensive_issues(all_files, issues, goal, attempt_tracker, rejected_files, round_number=attempt)
            
            if fixed_files:
                # ============================================
                # Phase B: 差分スキャンによる悪化検知（v2.0）
                # 目的: 変更範囲のみを再検出し、キャッシュとマージして判定
                # 効果: LLM検出のばらつきを局所化、「お化けIssue」を防止
                # ============================================
                REGRESSION_TOLERANCE = 0  # 🔧 Issue増加は許容しない（2→0に変更）
                
                logger.debug(f"[Regression Check] Checking for regression...")
                
                apply_fix = True  # デフォルトは適用
                post_fix_count = current_issue_count  # デフォルト値
                
                # 差分スキャンが有効かつキャッシュが存在する場合
                if self._differential_scan_enabled and self._issue_cache is not None:
                    # 1. 修正前後のファイルを比較して変更範囲を検出
                    modified_ranges = self._detect_modified_ranges_from_files(all_files, fixed_files)
                    
                    if modified_ranges:
                        logger.debug(f"[Differential Scan] Detected {len(modified_ranges)} modified range(s)")
                        
                        # 2. 変更範囲のみを再検出
                        new_issues = self._detect_issues_for_ranges(modified_ranges, fixed_files, goal)
                        
                        # 3. キャッシュとマージ
                        merged_issues, updated_cache = self._merge_issues(
                            self._issue_cache,
                            new_issues,
                            modified_ranges,
                            fixed_files
                        )
                        
                        post_fix_count = len(merged_issues)
                        
                        # 4. 判定
                        if post_fix_count > current_issue_count + REGRESSION_TOLERANCE:
                            logger.debug(f"[Regression Check] ❌ Fix worsened issues: {current_issue_count} -> {post_fix_count} (+{post_fix_count - current_issue_count})")
                            logger.debug(f"[Comprehensive Review] ⚠ Fix rejected due to regression (exceeded tolerance of {REGRESSION_TOLERANCE})")
                            apply_fix = False
                            # キャッシュは変更しない（ロールバック）
                        elif post_fix_count > current_issue_count:
                            logger.debug(f"[Regression Check] ⚠ Slight regression: {current_issue_count} -> {post_fix_count} (+{post_fix_count - current_issue_count}), within tolerance")
                            # 軽微な悪化 → キャッシュを更新して適用
                            self._issue_cache = updated_cache
                        else:
                            improvement = current_issue_count - post_fix_count
                            if improvement > 0:
                                logger.debug(f"[Regression Check] ✅ Improved: {current_issue_count} -> {post_fix_count} (-{improvement})")
                            else:
                                logger.debug(f"[Regression Check] ✅ No change: {current_issue_count} -> {post_fix_count}")
                            # 改善または同等 → キャッシュを更新
                            self._issue_cache = updated_cache
                    else:
                        # 変更範囲が検出されなかった場合、修正を適用
                        logger.debug(f"[Differential Scan] No changes detected, applying fix")
                else:
                    # 差分スキャン無効またはキャッシュなし → 従来方式（フォールバック）
                    logger.debug(f"[Regression Check] Using full scan (cache not available)")
                    post_fix_result = self._detect_comprehensive_issues(fixed_files, goal)
                    
                    if post_fix_result.get('error', False):
                        logger.debug(f"[Regression Check] Detection failed, applying fix anyway")
                    else:
                        post_fix_count = len(post_fix_result.get('issues', []))
                        
                        if post_fix_count > current_issue_count + REGRESSION_TOLERANCE:
                            logger.debug(f"[Regression Check] ❌ Fix worsened issues: {current_issue_count} -> {post_fix_count} (+{post_fix_count - current_issue_count})")
                            logger.debug(f"[Comprehensive Review] ⚠ Fix rejected due to regression (exceeded tolerance of {REGRESSION_TOLERANCE})")
                            apply_fix = False
                        elif post_fix_count > current_issue_count:
                            logger.debug(f"[Regression Check] ⚠ Slight regression: {current_issue_count} -> {post_fix_count} (+{post_fix_count - current_issue_count}), within tolerance")
                        else:
                            improvement = current_issue_count - post_fix_count
                            if improvement > 0:
                                logger.debug(f"[Regression Check] ✅ Improved: {current_issue_count} -> {post_fix_count} (-{improvement})")
                            else:
                                logger.debug(f"[Regression Check] ✅ No change: {current_issue_count} -> {post_fix_count}")
                
                if apply_fix:
                    all_files = fixed_files
                    logger.debug(f"[Comprehensive Review] Applied fixes (attempt {attempt + 1})")
                    logger.debug(f"    {Icon.SUCCESS.value} Issues fixed (attempt {attempt + 1}/{MAX_REVIEW_ATTEMPTS})")
                else:
                    # 悪化によりロールバック → ループ終了
                    logger.debug(f"[Comprehensive Review] Fix rolled back due to regression (attempt {attempt + 1})")
                    logger.debug(f"    {Icon.WARNING.value} Fix rejected - would worsen issues")
                    break
            else:
                logger.debug(f"[Comprehensive Review] Fix failed (attempt {attempt + 1})")
                logger.debug(f"    {Icon.WARNING.value} Could not fix all issues (attempt {attempt + 1}/{MAX_REVIEW_ATTEMPTS})")
                break
        
        # ⭐ Zen HUD: 最終Review issue数を記録
        # 差分スキャン対応: キャッシュが存在する場合はキャッシュを使用
        # これにより「お化けIssue」を防止し、一貫したIssue数を報告
        if self._differential_scan_enabled and self._issue_cache is not None:
            # キャッシュからIssue数を計算
            final_issue_count = sum(len(issues) for issues in self._issue_cache.values())
            self._zen_summary["review"]["final"] = final_issue_count
            # キャッシュからissuesリストを構築（ファイル単位ペナルティ用）
            cached_issues = []
            for file_path, issues in self._issue_cache.items():
                for issue in issues:
                    cached_issues.append({
                        'file': issue.file,
                        'line': issue.line,
                        'type': issue.issue_type,
                        'description': issue.description,
                        'fix': issue.fix
                    })
            self._zen_summary["review"]["issues"] = cached_issues
            self._zen_summary["review"]["fixed"] = (
                self._zen_summary["review"]["initial"] > 0 and 
                final_issue_count < self._zen_summary["review"]["initial"]
            )
            logger.debug(f"[Differential Scan] Final issue count from cache: {final_issue_count}")
        else:
            # 従来方式（フォールバック）: キャッシュなしの場合は全検出
            final_result = self._detect_comprehensive_issues(all_files, goal)
            if final_result.get('error', False):
                # エラー時は不明として記録
                self._zen_summary["review"]["final"] = -1
                self._zen_summary["review"]["issues"] = []  # エラー時は空リスト
                self._zen_summary["review"]["fixed"] = False
                logger.debug(f"[Comprehensive Review] ⚠ Final detection failed, marking as unknown")
            else:
                final_issues = final_result.get('issues', [])
                self._zen_summary["review"]["final"] = len(final_issues)
                self._zen_summary["review"]["issues"] = final_issues  # ファイル単位のissue情報を保存
                self._zen_summary["review"]["fixed"] = (
                    self._zen_summary["review"]["initial"] > 0 and 
                    len(final_issues) < self._zen_summary["review"]["initial"]
                )
        
        return all_files
    
    # ============================================================
    # 🆕 改修: 検出2レイヤ化
    # ============================================================
    def _detect_blocking_issues(
        self,
        all_files: Dict[str, str],
        goal: str
    ) -> Dict[str, Any]:
        """
        Layer 1: Blocking issues only (runtime errors, data corruption)
        
        検出対象（10項目）:
        9. Missing await on async calls
        10. Missing db.session.commit()
        11. Boundary conditions not handled
        13. Missing input validation
        14. References to non-existent config
        17. Library-specific API misuse
        18. Circular imports
        19. Security issues
        20. Resource leaks
        21. Type/return value mismatch
        """
        if not self.llm_manager:
            logger.debug("[Blocking Detection] LLM manager not available")
            return {'issues': [], 'error': False}
        
        files_content = ""
        for filepath, content in all_files.items():
            files_content += f"<file path='{filepath}'>\n{content}\n</file>\n\n"
        
        prompt = f"""You are a code quality reviewer. Analyze for BLOCKING issues only.
Focus ONLY on issues that cause runtime errors, data corruption, or security vulnerabilities.

**Original Goal:**
{goal}

**Project Files:**
{files_content}

**Check for these 10 BLOCKING issue types ONLY:**

9. Missing await on async calls, unhandled Promises
   → Causes: Runtime errors, race conditions
   
10. Missing db.session.commit() after database operations
    → Causes: Data not persisted, silent failures
    
11. Boundary conditions not handled (empty array, null, 0, undefined)
    → Causes: TypeError, crashes
    
13. Missing input validation (empty submission causes errors)
    → Causes: Runtime errors, data corruption
    
14. References to non-existent config/environment variables
    → Causes: KeyError, AttributeError
    
17. Library-specific API misuse (Marshmallow: required=True with load_default, etc.)
    → Causes: Runtime errors, unexpected behavior
    
18. Circular imports (ImportError: cannot import name)
    → Causes: Import failures
    
19. Security issues (SQL injection, XSS, plaintext passwords, hardcoded secrets)
    → Causes: Security vulnerabilities, data breaches
    
20. Resource leaks (open() without with, DB connection not closed)
    → Causes: Resource exhaustion
    
21. Type/return value mismatch (declared -> User but returns None)
    → Causes: TypeError at runtime

**DO NOT report:**
- TODO/FIXME comments (not blocking)
- Dead code or unused variables (not blocking)
- Debug code remaining (not blocking)
- Style issues (not blocking)
- UI/UX issues (not blocking unless crash)

**Output Format:**
Return a JSON array. Each issue must have:
- "file": filepath
- "line": line number (or null)
- "type": issue type number (9, 10, 11, 13, 14, 17, 18, 19, 20, or 21 ONLY)
- "description": specific problem
- "fix": suggested fix

If no BLOCKING issues found: return []

Return ONLY the JSON array, no other text."""

        try:
            logger.debug("[Blocking Detection] Scanning for runtime/security issues...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality expert. Detect ONLY blocking issues (runtime errors, security). Return valid JSON array.",
                max_tokens=6000
            )
            
            response_text = response.content if hasattr(response, 'content') else str(response)
            
            # JSON抽出
            json_match = re.search(r'```(?:json)?\s*(\[.*?\])\s*```', response_text, re.DOTALL)
            if json_match:
                json_str = json_match.group(1)
            else:
                json_match = re.search(r'(\[.*\])', response_text, re.DOTALL)
                if json_match:
                    json_str = json_match.group(1)
                else:
                    logger.debug("[Blocking Detection] No JSON found in response")
                    return {'issues': [], 'error': False}
            
            issues = json.loads(json_str)
            
            # Layer 1のタイプのみフィルタ（念のため）
            blocking_types = [9, 10, 11, 13, 14, 17, 18, 19, 20, 21]
            blocking_issues = [
                i for i in issues 
                if i.get('type') in blocking_types
            ]
            
            logger.debug(f"[Blocking Detection] Found {len(blocking_issues)} blocking issue(s)")
            return {'issues': blocking_issues, 'error': False}
            
        except json.JSONDecodeError as e:
            logger.debug(f"[Blocking Detection] JSON parse error: {e}")
            return {'issues': [], 'error': True, 'error_message': f'JSON parse error: {e}'}
        except Exception as e:
            logger.debug(f"[Blocking Detection] Error: {e}")
            return {'issues': [], 'error': True, 'error_message': str(e)}

    def _detect_comprehensive_issues(
        self,
        all_files: Dict[str, str],
        goal: str
    ) -> Dict[str, Any]:
        """
        25項目の問題検出
        
        検出項目:
        1. TODO/FIXME/未実装のまま残っている機能
        2. エラー処理が不完全（catchが空、console.logのみ等）
        3. 呼び出し元がない関数（デッドコード）
        4. 宣言されているが使われていない変数
        5. UIで操作できるが実際には動作しない機能
        6. 状態のリセット/初期化漏れ（終了後に初期状態に戻らない等）
        7. データの保存→読込の不整合（セーブできるがロードで復元されない等）
        8. 画面遷移の行き止まり（戻る手段がない、次に進めない）
        9. 非同期処理のawait漏れ、Promise未処理
        10. DB操作後のcommit漏れ（SQLAlchemy: db.session.commit()）
        11. 空配列/null/0/undefinedの境界条件で落ちる処理
        12. 処理フローの未完結（開始はあるが終了処理がない）
        13. 必須入力のバリデーション漏れ（空送信でエラー）
        14. 存在しない設定/環境変数の参照
        15. API呼び出し結果の未チェック（response.ok確認なし等）
        16. イベントハンドラの二重登録
        17. ライブラリ固有のAPI誤用（Marshmallow, SQLAlchemy, Flask等）
        18. 循環インポート（ImportError: cannot import name）
        19. セキュリティ問題（SQLi, XSS, 平文パスワード, ハードコード秘密鍵）
        20. リソースリーク（ファイル/DB接続/ソケットの未クローズ）
        21. 型/戻り値の不一致（-> User で return None等）
        22. OS依存/ハードコードパス（C:\\, /home/user/, 絶対パス）
        23. デバッグコード残存（print, console.log, debugger, breakpoint）
        24. 外部依存の未処理（タイムアウト未設定, リトライなし, フォールバックなし）
        
        Args:
            all_files: 生成されたファイル一式
            goal: 元のゴール
        
        Returns:
            Dict with keys:
                - 'issues': List of detected issues
                - 'error': bool indicating if detection failed
                - 'error_message': str (only present when error=True)
        """
        if not self.llm_manager:
            logger.debug("[Comprehensive Review] LLM manager not available")
            return {'issues': [], 'error': False}
        
        # ファイル内容を整形
        files_content = ""
        for filepath, content in all_files.items():
            files_content += f"<file path='{filepath}'>\n{content}\n</file>\n\n"
        
        prompt = f"""You are a code quality reviewer. Analyze the following project for issues that would prevent it from working correctly.

**Original Goal:**
{goal}

**Project Files:**
{files_content}

**Check for these 25 issue types:**
1. TODO/FIXME/unimplemented features remaining
2. Incomplete error handling (empty catch, console.log only)
3. Dead code (functions never called)
4. Unused variables
5. UI elements that don't actually work (buttons that do nothing meaningful)
6. State reset/initialization missing (e.g., game over doesn't reset to initial state)
7. Save/Load inconsistency (data saved but not properly restored on load)
8. Dead-end navigation (no way to go back or proceed)
9. Missing await on async calls, unhandled Promises
10. Missing db.session.commit() after database operations
11. Boundary conditions not handled (empty array, null, 0, undefined)
12. Incomplete process flow (start exists but no proper end/cleanup)
13. Missing input validation (empty submission causes errors)
14. References to non-existent config/environment variables
15. API response not checked (no response.ok verification)
16. Event handler double registration
17. Library-specific API misuse (e.g., Marshmallow: required=True with load_default, SQLAlchemy: invalid cascade, Flask: context issues)
18. Circular imports (ImportError: cannot import name from partially initialized module)
19. Security issues (SQL injection with f-strings, XSS with innerHTML/dangerouslySetInnerHTML, plaintext passwords, hardcoded secrets like SECRET_KEY="literal")
20. Resource leaks (open() without with statement, DB connection/cursor not closed, socket not closed)
21. Type/return value mismatch (function declared -> User but returns None, async function not awaited)
22. OS-dependent/hardcoded paths (C:\\, /home/user/, absolute paths, backslash in path strings)
23. Debug code remaining (print(), console.log(), debugger, breakpoint(), pdb.set_trace())
24. External dependency issues (requests without timeout, no error handling for network calls, no retry/fallback)
25. Stub/placeholder files (files with >80% comments and no real code, files containing "not used", "stub", "placeholder", or referencing other files as the "real" implementation)

**Output Format:**
Return a JSON array of issues found. Each issue should have:
- "file": filepath
- "line": line number (if identifiable, otherwise null)
- "type": issue type number (1-25)
- "description": specific description of the problem
- "fix": suggested fix

If no issues found, return an empty array: []

**Important:**
- Focus on issues that would cause runtime errors or broken functionality
- Ignore minor style issues
- Be specific about what's wrong and how to fix it

**CRITICAL EXCLUSIONS (These are NOT issues - Do NOT report):**

### Game/Interactive Application Patterns (NOT debug code):

**UI Elements (type 23 exclusion):**
- HUD displays (score, lives, stage, combo, level, timer, health bar) are INTENTIONAL game UI
- Game state text (GAME OVER, PAUSED, VICTORY, STAGE CLEAR, WARNING!, READY) are INTENTIONAL
- Score popups, combo indicators, floating damage numbers are INTENTIONAL feedback
- Power-up indicators, status effects, cooldown displays are INTENTIONAL
- DO NOT report these as "debug code remaining"

**Intentional Logging (type 23 exclusion):**
- console.error() → ALWAYS acceptable (error handling)
- console.warn() → ALWAYS acceptable (warnings)
- console.log() inside catch/error blocks → acceptable for diagnostics
- ONLY report console.log if it's clearly temporary: console.log('test'), console.log(123), console.log('debug')

**Game Loop Patterns (NOT issues):**
- requestAnimationFrame() recursive calls → standard game loop, NOT infinite loop
- setInterval/setTimeout for game timing → intentional, NOT resource leak
- Canvas rendering loops → required for games
- Particle system update loops → required for effects

**React/Vue/Angular Patterns (NOT unused code - type 3, 4 exclusion):**
- useState variables only appearing in JSX → they ARE used (rendered)
- useEffect hooks → they ARE used (side effects run)
- useRef values → they ARE used (DOM/value persistence)
- Event handler functions → called by framework, NOT dead code

**Event-Driven Architecture (NOT dead code - type 3 exclusion):**
- Functions only called by event listeners (onClick, onKeyDown) → they ARE called at runtime
- Functions only called by setTimeout/setInterval → they ARE called
- Functions only called by requestAnimationFrame → they ARE called
- Callback functions for promises/async → they ARE called

**Intentional Patterns (NOT issues):**
- Empty catch blocks with explicit comment (// intentionally ignored) → acceptable
- Optional chaining (?.) returning undefined → intentional fallback
- Default parameter values → intentional defaults

### Type 23 (Debug Code) Clarification:

**IS debug code (REPORT):**
- console.log('test'), console.log('here'), console.log('debug')
- console.log(variable) with no context (random debugging)
- debugger; statements
- breakpoint() calls

**IS NOT debug code (DO NOT REPORT):**
- console.log('Game started') → user feedback
- console.error('Failed to load:', error) → error handling
- Any logging inside try/catch blocks
- HUD rendering code that displays game state

Return ONLY the JSON array, no other text."""

        import json
        import re
        
        try:
            logger.debug("[Comprehensive Review] Sending detection request to LLM...")
            logger.debug(f"      Analyzing code for issues...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality expert. Detect issues that prevent code from working correctly. Return ONLY valid JSON.",
                max_tokens=8000
            )
            
            logger.debug("[Comprehensive Review] LLM detection response received")
            
            if hasattr(response, 'content'):
                response_text = response.content
            else:
                response_text = str(response)
            
            # JSON抽出
            # JSONブロックを探す
            json_match = re.search(r'```(?:json)?\s*(\[.*?\])\s*```', response_text, re.DOTALL)
            if json_match:
                json_str = json_match.group(1)
            else:
                # ```なしの場合、配列を直接探す
                json_match = re.search(r'\[.*\]', response_text, re.DOTALL)
                if json_match:
                    json_str = json_match.group(0)
                else:
                    logger.debug("[Comprehensive Review] No JSON array found in response")
                    return {'issues': [], 'error': False}
            
            issues = json.loads(json_str)
            
            if not isinstance(issues, list):
                logger.debug("[Comprehensive Review] Response is not a list")
                return {'issues': [], 'error': False}
            
            logger.debug(f"[Comprehensive Review] Detected {len(issues)} issue(s)")
            return {'issues': issues, 'error': False}
            
        except json.JSONDecodeError as e:
            logger.debug(f"[Comprehensive Review] JSON parse error: {e}")
            return {'issues': [], 'error': True, 'error_message': f'JSON parse error: {e}'}
        except Exception as e:
            logger.debug(f"[Comprehensive Review] Detection error: {e}")
            return {'issues': [], 'error': True, 'error_message': str(e)}
    
    # ============================================================
    # 差分スキャン: Issueキャッシュ管理
    # ============================================================
    
    def _get_content_hash(self, content: str, line_start: int, line_end: int) -> str:
        """
        指定行範囲のコンテンツハッシュを計算
        
        変更検知用。同じハッシュ = 同じコード = キャッシュ有効。
        
        Args:
            content: ファイル全体の内容
            line_start: 開始行（1-indexed）
            line_end: 終了行（1-indexed、inclusive）
            
        Returns:
            SHA256ハッシュの先頭16文字
        """
        import hashlib
        
        lines = content.split('\n')
        # 1-indexed → 0-indexed変換、範囲外対策
        start_idx = max(0, line_start - 1)
        end_idx = min(len(lines), line_end)
        
        target_lines = lines[start_idx:end_idx]
        target_content = '\n'.join(target_lines)
        
        return hashlib.sha256(target_content.encode('utf-8')).hexdigest()[:16]
    
    def _cache_issues(
        self,
        issues: List[Dict[str, Any]],
        all_files: Dict[str, str]
    ) -> Dict[str, List[CachedIssue]]:
        """
        Issue検出結果をキャッシュに保存
        
        各Issueに該当行のcontent_hashを付与し、ファイルごとにグルーピング。
        
        Args:
            issues: _detect_comprehensive_issuesの戻り値['issues']
            all_files: ファイルパス→内容のマップ
            
        Returns:
            Dict[str, List[CachedIssue]]: ファイルパス→CachedIssueリスト
        """
        cache: Dict[str, List[CachedIssue]] = {}
        
        for issue in issues:
            filepath = issue.get('file', '')
            line = issue.get('line')
            issue_type = issue.get('type', 0)
            description = issue.get('description', '')
            fix = issue.get('fix', '')
            
            # ファイル内容を取得
            content = all_files.get(filepath, '')
            
            # 行番号がある場合、その周辺（±2行）のハッシュを計算
            # 行番号がない場合、ファイル全体のハッシュを計算
            if line is not None and isinstance(line, int) and line > 0:
                line_start = max(1, line - 2)
                line_end = line + 2
                content_hash = self._get_content_hash(content, line_start, line_end)
            else:
                # ファイル全体のハッシュ
                import hashlib
                content_hash = hashlib.sha256(content.encode('utf-8')).hexdigest()[:16]
            
            # CachedIssueを作成
            cached = CachedIssue(
                file=filepath,
                line=line if isinstance(line, int) else None,
                line_end=None,  # 単一行のIssueの場合
                issue_type=issue_type if isinstance(issue_type, int) else 0,
                description=description,
                fix=fix,
                content_hash=content_hash
            )
            
            # ファイルごとにグルーピング
            if filepath not in cache:
                cache[filepath] = []
            cache[filepath].append(cached)
        
        logger.debug(f"[Differential Scan] Cached {len(issues)} issue(s) across {len(cache)} file(s)")
        
        return cache
    
    def _clear_issue_cache(self):
        """
        Issueキャッシュをクリア
        
        新しい修正サイクルの開始時や、大幅な変更があった場合に呼び出す。
        """
        self._issue_cache = None
        logger.debug("[Differential Scan] Issue cache cleared")
    
    def _is_line_in_modified_range(
        self,
        line: Optional[int],
        modified_ranges: List[ModifiedRange],
        filepath: str,
        margin: int = 3  # 🔧 専門家推奨: 5→3行に縮小（キャッシュヒット率向上）
    ) -> bool:
        """
        指定行が変更範囲内かどうかを判定
        
        Args:
            line: 判定対象の行番号（Noneの場合はファイル全体→常にTrue）
            modified_ranges: 変更範囲リスト
            filepath: ファイルパス
            margin: 前後のマージン（デフォルト3行）
            
        Returns:
            True: 変更範囲内（再検出が必要）
            False: 変更範囲外（キャッシュを使用可能）
        """
        # 行番号がない場合、ファイル全体のIssue → 再検出必要
        if line is None:
            return True
        
        for mr in modified_ranges:
            if mr.file != filepath:
                continue
            
            # マージンを含めた範囲判定
            range_start = max(1, mr.start_line - margin)
            range_end = mr.end_line + margin
            
            if range_start <= line <= range_end:
                return True
        
        return False
    
    def _extract_modified_ranges(
        self,
        diff_content: str,
        filepath: str
    ) -> List[ModifiedRange]:
        """
        unified diffから変更範囲を抽出
        
        DESIGN CHANGE v2.1が出力するunified diff形式を解析し、
        変更された行範囲のリストを返す。
        
        Args:
            diff_content: unified diff形式の差分
            filepath: 対象ファイルパス
            
        Returns:
            List[ModifiedRange]: 変更範囲のリスト
            
        Example:
            入力:
            @@ -20,6 +20,8 @@
             def main():
            +    if not config.API_KEY:
            +        raise ValueError("API_KEY required")
                 app.run()
            
            出力:
            [ModifiedRange(file="main.py", start_line=20, end_line=27, diff_content="...")]
        """
        import re
        
        modified_ranges: List[ModifiedRange] = []
        
        # ハンクヘッダーのパターン: @@ -old_start,old_count +new_start,new_count @@
        hunk_pattern = r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@'
        
        diff_lines = diff_content.split('\n')
        i = 0
        
        while i < len(diff_lines):
            line = diff_lines[i]
            
            # ハンクヘッダーを検出
            hunk_match = re.match(hunk_pattern, line)
            if hunk_match:
                # 新ファイル側の開始行と行数を取得
                new_start = int(hunk_match.group(3))
                new_count = int(hunk_match.group(4)) if hunk_match.group(4) else 1
                
                # ハンク内容を収集
                hunk_content_lines = [line]  # ヘッダーも含める
                i += 1
                
                # 追加行数と削除行数をカウント
                added_lines = 0
                deleted_lines = 0
                
                while i < len(diff_lines) and not diff_lines[i].startswith('@@'):
                    current_line = diff_lines[i]
                    hunk_content_lines.append(current_line)
                    
                    if current_line.startswith('+') and not current_line.startswith('+++'):
                        added_lines += 1
                    elif current_line.startswith('-') and not current_line.startswith('---'):
                        deleted_lines += 1
                    
                    i += 1
                
                # 変更範囲を計算（新ファイル基準）
                # 追加行がある場合、その範囲を含める
                actual_end = new_start + new_count - 1 + added_lines
                
                modified_range = ModifiedRange(
                    file=filepath,
                    start_line=new_start,
                    end_line=actual_end,
                    diff_content='\n'.join(hunk_content_lines)
                )
                modified_ranges.append(modified_range)
                
                logger.debug(f"[Differential Scan] Extracted range: {filepath} L{new_start}-L{actual_end}")
            else:
                i += 1
        
        return modified_ranges
    
    def _detect_issues_for_ranges(
        self,
        modified_ranges: List[ModifiedRange],
        all_files: Dict[str, str],
        goal: str,
        margin: int = 3  # 🔧 ±3行で統一
    ) -> List[Dict[str, Any]]:
        """
        変更範囲のみをLLMで検出
        
        全ファイルを再検出するのではなく、変更された範囲（±margin行）
        のみを対象にIssue検出を行う。トークン消費を削減し、
        「お化けIssue」を防ぐ。
        
        Args:
            modified_ranges: 変更範囲リスト
            all_files: ファイルパス→内容のマップ
            goal: 元のゴール
            margin: 前後のマージン（デフォルト3行）
            
        Returns:
            検出されたIssueのリスト
        """
        import json
        import re
        
        if not modified_ranges:
            logger.debug("[Differential Scan] No modified ranges to scan")
            return []
        
        if not self.llm_manager:
            logger.debug("[Differential Scan] LLM manager not available")
            return []
        
        # 変更範囲ごとにコードスニペットを収集
        snippets_content = ""
        for mr in modified_ranges:
            content = all_files.get(mr.file, '')
            if not content:
                continue
            
            lines = content.split('\n')
            
            # マージンを含めた範囲を抽出
            start_idx = max(0, mr.start_line - 1 - margin)
            end_idx = min(len(lines), mr.end_line + margin)
            
            snippet_lines = lines[start_idx:end_idx]
            snippet = '\n'.join(f"{start_idx + i + 1}: {line}" for i, line in enumerate(snippet_lines))
            
            snippets_content += f"""
<modified_section file="{mr.file}" lines="{mr.start_line}-{mr.end_line}">
{snippet}
</modified_section>

"""
        
        prompt = f"""You are a code quality reviewer. Analyze ONLY the modified sections below for issues.

**IMPORTANT**: You are reviewing ONLY the changed code sections, not the entire project.
Focus on issues that could have been introduced or affected by these changes.

**Original Goal:**
{goal}

**Modified Sections:**
{snippets_content}

**Check for these issue types in the modified sections:**
1. TODO/FIXME/unimplemented features
2. Incomplete error handling
3. Unused variables (in the modified section)
4. Missing await on async calls
5. Boundary conditions not handled
6. Missing input validation
7. API response not checked
8. Type/return value mismatch
9. Security issues (SQL injection, XSS, hardcoded secrets)
10. Resource leaks

**Output Format:**
Return a JSON array of issues found. Each issue should have:
- "file": filepath
- "line": line number (must be within the modified range)
- "type": issue type number
- "description": specific description
- "fix": suggested fix

If no issues found in the modified sections, return an empty array: []

Return ONLY the JSON array, no other text."""

        try:
            logger.debug(f"[Differential Scan] Scanning {len(modified_ranges)} modified range(s)...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality expert. Detect issues ONLY in the modified sections. Return ONLY valid JSON.",
                max_tokens=4000  # 範囲限定なのでトークン削減
            )
            
            if hasattr(response, 'content'):
                response_text = response.content
            else:
                response_text = str(response)
            
            # JSON抽出
            json_match = re.search(r'```(?:json)?\s*(\[.*?\])\s*```', response_text, re.DOTALL)
            if json_match:
                json_str = json_match.group(1)
            else:
                json_match = re.search(r'\[.*\]', response_text, re.DOTALL)
                if json_match:
                    json_str = json_match.group(0)
                else:
                    logger.debug("[Differential Scan] No JSON array found in response")
                    return []
            
            issues = json.loads(json_str)
            
            if not isinstance(issues, list):
                logger.debug("[Differential Scan] Response is not a list")
                return []
            
            # 変更範囲外のIssueをフィルタリング（LLMが範囲外を報告した場合）
            filtered_issues = []
            for issue in issues:
                issue_file = issue.get('file', '')
                issue_line = issue.get('line')
                
                # 行番号がある場合、変更範囲内かチェック
                if issue_line is not None:
                    in_range = False
                    for mr in modified_ranges:
                        if mr.file == issue_file:
                            if mr.start_line - margin <= issue_line <= mr.end_line + margin:
                                in_range = True
                                break
                    
                    if not in_range:
                        logger.debug(f"[Differential Scan] Filtered out-of-range issue: {issue_file}:{issue_line}")
                        continue
                
                filtered_issues.append(issue)
            
            logger.debug(f"[Differential Scan] Detected {len(filtered_issues)} issue(s) in modified ranges")
            return filtered_issues
            
        except json.JSONDecodeError as e:
            logger.debug(f"[Differential Scan] JSON parse error: {e}")
            return []
        except Exception as e:
            logger.debug(f"[Differential Scan] Detection error: {e}")
            return []
    
    def _merge_issues(
        self,
        cache: Dict[str, List[CachedIssue]],
        new_issues: List[Dict[str, Any]],
        modified_ranges: List[ModifiedRange],
        all_files: Dict[str, str],
        margin: int = 3  # 🔧 ±3行で統一
    ) -> Tuple[List[Dict[str, Any]], Dict[str, List[CachedIssue]]]:
        """
        キャッシュと新規検出結果をマージ
        
        ルール:
        1. 変更範囲外のIssue → キャッシュから取得（content_hash照合）
        2. 変更範囲内のIssue → 新規検出結果を使用
        3. 重複排除（同一行の同一Issue種別）
        
        Args:
            cache: 既存のIssueキャッシュ
            new_issues: _detect_issues_for_ranges()の結果
            modified_ranges: 変更範囲リスト
            all_files: ファイルパス→内容のマップ
            margin: マージン（_is_line_in_modified_rangeで使用）★3行に統一
            
        Returns:
            Tuple[List[Dict], Dict[str, List[CachedIssue]]]:
                - マージ後のIssueリスト
                - 更新後のキャッシュ
        """
        merged_issues: List[Dict[str, Any]] = []
        updated_cache: Dict[str, List[CachedIssue]] = {}
        
        # 変更されたファイルのセット
        modified_files = set(mr.file for mr in modified_ranges)
        
        # 1. キャッシュからIssueを復元（変更範囲外のみ）
        for filepath, cached_issues in cache.items():
            updated_cache[filepath] = []
            
            for cached in cached_issues:
                # このIssueが変更範囲内かチェック
                in_modified_range = self._is_line_in_modified_range(
                    cached.line,
                    modified_ranges,
                    filepath,
                    margin
                )
                
                if in_modified_range:
                    # 変更範囲内 → キャッシュから除外（新規検出結果を使う）
                    logger.debug(f"[Differential Scan] Invalidated cached issue: {filepath}:{cached.line}")
                    continue
                
                # 変更範囲外 → content_hashを照合
                content = all_files.get(filepath, '')
                if cached.line is not None:
                    current_hash = self._get_content_hash(
                        content,
                        max(1, cached.line - 2),
                        cached.line + 2
                    )
                else:
                    import hashlib
                    current_hash = hashlib.sha256(content.encode('utf-8')).hexdigest()[:16]
                
                if current_hash == cached.content_hash:
                    # ハッシュ一致 → キャッシュ有効、Issueを復元
                    merged_issues.append({
                        'file': cached.file,
                        'line': cached.line,
                        'type': cached.issue_type,
                        'description': cached.description,
                        'fix': cached.fix
                    })
                    updated_cache[filepath].append(cached)
                else:
                    # ハッシュ不一致 → コードが変更された、キャッシュ無効
                    logger.debug(f"[Differential Scan] Hash mismatch, invalidated: {filepath}:{cached.line}")
        
        # 2. 新規検出結果を追加
        for issue in new_issues:
            filepath = issue.get('file', '')
            line = issue.get('line')
            issue_type = issue.get('type', 0)
            description = issue.get('description', '')
            fix = issue.get('fix', '')
            
            # 重複チェック（同一ファイル・同一行・同一種別）
            is_duplicate = False
            for existing in merged_issues:
                if (existing.get('file') == filepath and
                    existing.get('line') == line and
                    existing.get('type') == issue_type):
                    is_duplicate = True
                    break
            
            if is_duplicate:
                logger.debug(f"[Differential Scan] Skipped duplicate issue: {filepath}:{line}")
                continue
            
            # Issueを追加
            merged_issues.append(issue)
            
            # 新規Issueをキャッシュに追加
            content = all_files.get(filepath, '')
            if line is not None and isinstance(line, int) and line > 0:
                content_hash = self._get_content_hash(content, max(1, line - 2), line + 2)
            else:
                import hashlib
                content_hash = hashlib.sha256(content.encode('utf-8')).hexdigest()[:16]
            
            cached_issue = CachedIssue(
                file=filepath,
                line=line if isinstance(line, int) else None,
                line_end=None,
                issue_type=issue_type if isinstance(issue_type, int) else 0,
                description=description,
                fix=fix,
                content_hash=content_hash
            )
            
            if filepath not in updated_cache:
                updated_cache[filepath] = []
            updated_cache[filepath].append(cached_issue)
        
        # 空のリストを持つキーを削除
        updated_cache = {k: v for k, v in updated_cache.items() if v}
        
        logger.debug(f"[Differential Scan] Merged: {len(merged_issues)} total issues "
                    f"(cached: {sum(len(v) for v in cache.values())}, new: {len(new_issues)})")
        
        return merged_issues, updated_cache
    
    def _detect_modified_ranges_from_files(
        self,
        original_files: Dict[str, str],
        modified_files: Dict[str, str]
    ) -> List[ModifiedRange]:
        """
        修正前後のファイルを比較して変更範囲を検出
        
        unified diffを使わずに、修正前後のコードを直接比較して
        変更された行範囲を特定する。
        
        Args:
            original_files: 修正前のファイルマップ
            modified_files: 修正後のファイルマップ
            
        Returns:
            List[ModifiedRange]: 検出された変更範囲
        """
        import difflib
        
        modified_ranges: List[ModifiedRange] = []
        
        for filepath in modified_files:
            original = original_files.get(filepath, '')
            modified = modified_files.get(filepath, '')
            
            if original == modified:
                continue  # 変更なし
            
            original_lines = original.split('\n')
            modified_lines = modified.split('\n')
            
            # difflib.SequenceMatcher で変更箇所を検出
            matcher = difflib.SequenceMatcher(None, original_lines, modified_lines)
            
            for tag, i1, i2, j1, j2 in matcher.get_opcodes():
                if tag == 'equal':
                    continue
                
                # replace, insert, delete いずれも変更として扱う
                # 新ファイル側の行番号を使用（1-indexed）
                start_line = j1 + 1
                end_line = max(j2, j1 + 1)  # 空範囲を防ぐ
                
                # 変更内容を抽出
                if tag == 'replace':
                    diff_content = f"Replaced lines {i1+1}-{i2} with lines {j1+1}-{j2}"
                elif tag == 'insert':
                    diff_content = f"Inserted lines {j1+1}-{j2}"
                else:  # delete
                    diff_content = f"Deleted lines {i1+1}-{i2}"
                    # 削除の場合、削除位置の周辺を影響範囲とする
                    start_line = max(1, j1)
                    end_line = max(1, j1 + 1)
                
                modified_range = ModifiedRange(
                    file=filepath,
                    start_line=start_line,
                    end_line=end_line,
                    diff_content=diff_content
                )
                modified_ranges.append(modified_range)
                
                logger.debug(f"[Differential Scan] Detected change: {filepath} L{start_line}-L{end_line} ({tag})")
        
        # 隣接する範囲をマージ（10行以内なら結合）
        merged = self._merge_adjacent_ranges(modified_ranges)
        
        return merged
    
    def _merge_adjacent_ranges(
        self,
        ranges: List[ModifiedRange],
        gap_threshold: int = 10
    ) -> List[ModifiedRange]:
        """
        隣接する変更範囲をマージ
        
        Args:
            ranges: 変更範囲リスト
            gap_threshold: この行数以下のギャップなら結合
            
        Returns:
            マージ後の変更範囲リスト
        """
        if not ranges:
            return []
        
        # ファイルごとにグルーピング
        by_file: Dict[str, List[ModifiedRange]] = {}
        for r in ranges:
            if r.file not in by_file:
                by_file[r.file] = []
            by_file[r.file].append(r)
        
        merged: List[ModifiedRange] = []
        
        for filepath, file_ranges in by_file.items():
            # 開始行でソート
            sorted_ranges = sorted(file_ranges, key=lambda r: r.start_line)
            
            current = sorted_ranges[0]
            
            for next_range in sorted_ranges[1:]:
                # 隣接または重複している場合はマージ
                if next_range.start_line <= current.end_line + gap_threshold:
                    # 範囲を拡張
                    current = ModifiedRange(
                        file=filepath,
                        start_line=current.start_line,
                        end_line=max(current.end_line, next_range.end_line),
                        diff_content=current.diff_content + "\n" + next_range.diff_content
                    )
                else:
                    merged.append(current)
                    current = next_range
            
            merged.append(current)
        
        return merged
    
    # ============================================================
    # FixAttemptTracker: Track fix attempts per file
    # Max 2 attempts per file, 3rd attempt forces design change
    # ============================================================
    class FixAttemptTracker:
        """Track fix attempts per file to prevent Whack-a-mole problem"""
        
        def __init__(self, max_attempts: int = 2):
            self.attempts_per_file: Dict[str, int] = {}
            self.max_attempts = max_attempts
            self.issue_history: Dict[str, List[str]] = {}
        
        def record_attempt(self, filepath: str, issue_ids: List[str]):
            """Record a fix attempt"""
            self.attempts_per_file[filepath] = self.attempts_per_file.get(filepath, 0) + 1
            if filepath not in self.issue_history:
                self.issue_history[filepath] = []
            self.issue_history[filepath].extend(issue_ids)
        
        def get_attempt_count(self, filepath: str) -> int:
            """Get current fix attempt count"""
            return self.attempts_per_file.get(filepath, 0)
        
        def needs_design_change(self, filepath: str) -> bool:
            """If fixed 2+ times but issues remain, design change is required"""
            return self.get_attempt_count(filepath) >= self.max_attempts
        
        def get_previous_issues(self, filepath: str) -> List[str]:
            """Get previous issue IDs"""
            return self.issue_history.get(filepath, [])
    
    # ============================================
    # 🆕 依存関係分析（Cross-File Dependency Analysis）
    # 研究根拠: AlphaCodium (arXiv:2401.08500), Aider Benchmarks
    # 目的: モグラ叩き現象を軽減するため、ファイル修正時に
    #       他ファイルとの依存関係情報をLLMに提供する
    # ============================================
    
    def _extract_function_snippet(self, content: str, func_name: str) -> str:
        """
        関数の最初の数行を抽出（シグネチャ+概要）
        
        Args:
            content: ファイル内容
            func_name: 関数名
        
        Returns:
            関数のスニペット（最大200文字）
        """
        import re
        
        if not content or not func_name:
            return ""
        
        # JavaScript: function funcName(...) { ... }
        js_pattern = rf"((?:async\s+)?function\s+{re.escape(func_name)}\s*\([^)]*\)\s*\{{)"
        match = re.search(js_pattern, content)
        if match:
            start = match.start()
            snippet = content[start:start + 200]
            last_newline = snippet.rfind('\n')
            if last_newline > 50:
                snippet = snippet[:last_newline]
            return snippet.strip()
        
        # JavaScript: const funcName = (...) => { ... }
        js_arrow_pattern = rf"((?:const|let|var)\s+{re.escape(func_name)}\s*=\s*(?:async\s*)?\([^)]*\)\s*=>)"
        match = re.search(js_arrow_pattern, content)
        if match:
            start = match.start()
            snippet = content[start:start + 200]
            last_newline = snippet.rfind('\n')
            if last_newline > 50:
                snippet = snippet[:last_newline]
            return snippet.strip()
        
        # JavaScript: ES6 class method - methodName(...) {
        js_class_pattern = rf"((?:async\s+)?{re.escape(func_name)}\s*\([^)]*\)\s*\{{)"
        match = re.search(js_class_pattern, content)
        if match:
            start = match.start()
            snippet = content[start:start + 200]
            last_newline = snippet.rfind('\n')
            if last_newline > 50:
                snippet = snippet[:last_newline]
            return snippet.strip()
        
        # Python: def func_name(...):
        py_pattern = rf"((?:async\s+)?def\s+{re.escape(func_name)}\s*\([^)]*\):)"
        match = re.search(py_pattern, content)
        if match:
            start = match.start()
            snippet = content[start:start + 200]
            last_newline = snippet.rfind('\n')
            if last_newline > 50:
                snippet = snippet[:last_newline]
            return snippet.strip()
        
        return ""
    
    def _analyze_file_dependencies(
        self,
        all_files: Dict[str, str]
    ) -> Dict[str, Dict[str, Any]]:
        """
        全ファイル間の依存関係を分析
        
        研究根拠:
        - AlphaCodium: コンテキスト4000トークン超で精度低下
        - Aider: 関係ないファイルを追加するとLLMが混乱
        
        そのため、ファイル全体ではなく「関連部分のみ」を抽出するための
        依存関係情報を収集する。
        
        Args:
            all_files: 全ファイル {filepath: content}
        
        Returns:
            Dict[filepath, DependencyInfo]: 各ファイルの依存関係情報
            DependencyInfo = {
                'shared_dom': Dict[str, List[str]],       # DOM ID → 他の使用ファイル
                'duplicate_functions': Dict[str, List[str]], # 関数名 → 他の定義ファイル
                'related_files': List[str]                # 関連ファイルのリスト
            }
        """
        import re
        
        # ========================================
        # Step 1: 各ファイルからDOM参照を抽出
        # ========================================
        dom_refs_by_file: Dict[str, Set[str]] = {}
        
        dom_patterns = [
            r"getElementById\s*\(\s*['\"]([^'\"]+)['\"]\s*\)",
            r"getElementById\s*\(\s*`([^`$]+)`\s*\)",  # テンプレートリテラル（変数なし）
            r"querySelector\s*\(\s*['\"]#([^'\"]+)['\"]\s*\)",
        ]
        
        for filepath, content in all_files.items():
            if not any(filepath.endswith(ext) for ext in ['.js', '.html', '.htm', '.jsx', '.tsx']):
                continue
            
            refs = set()
            for pattern in dom_patterns:
                refs.update(re.findall(pattern, content))
            
            if refs:
                dom_refs_by_file[filepath] = refs
        
        # ========================================
        # Step 2: 各ファイルから関数定義を抽出
        # ========================================
        func_defs_by_file: Dict[str, Set[str]] = {}
        
        js_func_patterns = [
            r"(?:async\s+)?function\s+(\w+)\s*\(",
            r"(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>",
            r"(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?function",
            # 🆕 ES6クラスメソッド: methodName(...) { または async methodName(...) {
            r"^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{",
        ]
        
        py_func_patterns = [
            r"(?:async\s+)?def\s+(\w+)\s*\(",
        ]
        
        # 除外する一般的な名前（誤検出防止）- 拡張版
        common_names = {
            # 🔴 JavaScript/Python予約語（forループ等の誤検出防止）
            'for', 'if', 'else', 'while', 'switch', 'case', 'default',
            'try', 'catch', 'finally', 'throw', 'with', 'do',
            'return', 'break', 'continue', 'new', 'this', 'super',
            'class', 'extends', 'import', 'export', 'from', 'as',
            'async', 'await', 'yield', 'typeof', 'instanceof', 'in',
            'let', 'const', 'var', 'function',
            # 🔴 Python特殊メソッド（__init__等の誤検出防止）
            '__init__', '__new__', '__del__', '__repr__', '__str__',
            '__call__', '__len__', '__iter__', '__next__', '__getitem__',
            '__setitem__', '__delitem__', '__contains__', '__enter__', '__exit__',
            # 一般的なメソッド名
            'init', 'initialize', 'setup', 'main', 'run', 'start', 'stop',
            'get', 'set', 'update', 'render', 'load', 'save',
            'constructor', 'toString', 'valueOf',
            # 🔴 一般的なセットアップ/ヘルスチェック（誤検出防止）
            'setupEventListeners', 'addEventListener', 'removeEventListener',
            'health_check', 'healthCheck', 'ping', 'status',
            # イベントハンドラ
            'handleClick', 'handleChange', 'handleSubmit', 'handleError',
            'onClick', 'onChange', 'onSubmit', 'onLoad', 'onError',
            # Reactライフサイクル
            'componentDidMount', 'componentWillUnmount', 'componentDidUpdate',
            'useEffect', 'useState', 'useCallback', 'useMemo',
            # 一般的な操作
            'fetch', 'parse', 'format', 'validate', 'process',
            'create', 'delete', 'remove', 'add', 'find', 'filter',
            # 状態管理
            'show', 'hide', 'toggle', 'enable', 'disable',
            'open', 'close', 'reset', 'clear', 'refresh',
            # 接続・バインド
            'connect', 'disconnect', 'bind', 'unbind', 'attach', 'detach',
            # その他
            'destroy', 'dispose', 'cleanup', 'teardown',
            # 短すぎる名前（2文字以下は除外）
        }
        
        for filepath, content in all_files.items():
            is_python = filepath.endswith('.py')
            is_js = any(filepath.endswith(ext) for ext in ['.js', '.jsx', '.ts', '.tsx'])
            
            if not (is_python or is_js):
                continue
            
            patterns = py_func_patterns if is_python else js_func_patterns
            defs = set()
            
            for pattern in patterns:
                matches = re.findall(pattern, content, re.MULTILINE)
                # 一般的な名前と短すぎる名前を除外
                defs.update(m for m in matches if m not in common_names and len(m) > 2)
            
            if defs:
                func_defs_by_file[filepath] = defs
        
        # ========================================
        # Step 3: 共有DOM要素を特定
        # ========================================
        dom_to_files: Dict[str, List[str]] = {}
        
        for filepath, refs in dom_refs_by_file.items():
            for dom_id in refs:
                if dom_id not in dom_to_files:
                    dom_to_files[dom_id] = []
                dom_to_files[dom_id].append(filepath)
        
        # 複数ファイルで参照されるもののみ
        shared_dom = {dom_id: files for dom_id, files in dom_to_files.items() if len(files) > 1}
        
        # ========================================
        # Step 4: 重複関数を特定
        # ========================================
        func_to_files: Dict[str, List[str]] = {}
        
        for filepath, defs in func_defs_by_file.items():
            for func_name in defs:
                if func_name not in func_to_files:
                    func_to_files[func_name] = []
                func_to_files[func_name].append(filepath)
        
        # 複数ファイルで定義されるもののみ
        duplicate_funcs = {func: files for func, files in func_to_files.items() if len(files) > 1}
        
        # ========================================
        # Step 5: 各ファイルごとに依存関係情報を構築
        # ========================================
        dependency_info: Dict[str, Dict[str, Any]] = {}
        
        for filepath in all_files.keys():
            # このファイルが関係する共有DOM
            file_shared_dom = {}
            for dom_id, files in shared_dom.items():
                if filepath in files:
                    other_files = [f for f in files if f != filepath]
                    if other_files:
                        file_shared_dom[dom_id] = other_files
            
            # このファイルが関係する重複関数
            file_duplicate_funcs = {}
            for func_name, files in duplicate_funcs.items():
                if filepath in files:
                    other_files = [f for f in files if f != filepath]
                    if other_files:
                        file_duplicate_funcs[func_name] = other_files
            
            # 関連ファイルのリスト（重複排除）
            related_files = set()
            for files in file_shared_dom.values():
                related_files.update(files)
            for files in file_duplicate_funcs.values():
                related_files.update(files)
            
            dependency_info[filepath] = {
                'shared_dom': file_shared_dom,
                'duplicate_functions': file_duplicate_funcs,
                'related_files': sorted(related_files)
            }
        
        # ログ出力
        dep_count = sum(1 for info in dependency_info.values() 
                        if info['shared_dom'] or info['duplicate_functions'])
        logger.debug(f"[Dependency Analysis] Analyzed {len(all_files)} files, {dep_count} have cross-file dependencies")
        
        if shared_dom:
            logger.debug(f"[Dependency Analysis] Shared DOM: {list(shared_dom.keys())[:5]}")
        if duplicate_funcs:
            logger.debug(f"[Dependency Analysis] Duplicate funcs: {list(duplicate_funcs.keys())[:5]}")
        
        return dependency_info
    
    def _extract_dependency_context(
        self,
        filepath: str,
        all_files: Dict[str, str],
        dependency_info: Dict[str, Any]
    ) -> str:
        """
        指定ファイルの修正に必要な依存関係コンテキストを生成
        
        研究根拠:
        - AlphaCodium: 4000トークン以内に抑える
        - ファイル全体ではなく関連部分のみを提供
        
        Args:
            filepath: 対象ファイルパス
            all_files: 全ファイル
            dependency_info: このファイルの依存関係情報
        
        Returns:
            依存関係コンテキスト文字列（最大2000文字 ≈ 500トークン）
        """
        shared_dom = dependency_info.get('shared_dom', {})
        duplicate_funcs = dependency_info.get('duplicate_functions', {})
        
        # 依存関係がなければ空文字列
        if not shared_dom and not duplicate_funcs:
            return ""
        
        lines = []
        lines.append("**【Cross-File Dependencies - IMPORTANT】**")
        lines.append("This file has dependencies with other files. Consider these when fixing:")
        lines.append("")
        
        # ========================================
        # 1. 共有DOM要素
        # ========================================
        if shared_dom:
            lines.append("**1. Shared DOM Elements:**")
            for dom_id, other_files in list(shared_dom.items())[:5]:  # 最大5個
                files_str = ', '.join(other_files[:3])  # 最大3ファイル
                if len(other_files) > 3:
                    files_str += f" (+{len(other_files) - 3} more)"
                lines.append(f"   - `#{dom_id}`: also used in {files_str}")
            # 🆕 Phase A - P8: 行動指示の強化
            lines.append("   → **COORDINATION REQUIRED**: Ensure consistent ID names.")
            lines.append("   → Do NOT rename these IDs - it will break other files.")
            lines.append("   → If modifying DOM structure, ensure changes are compatible with all using files.")
            lines.append("")
        
        # ========================================
        # 2. 重複関数（最重要）
        # ========================================
        if duplicate_funcs:
            lines.append("**2. ⚠️ DUPLICATE FUNCTIONS (MUST RESOLVE):**")
            
            for func_name, other_files in list(duplicate_funcs.items())[:3]:  # 最大3関数
                lines.append(f"   - `{func_name}()` is also defined in:")
                
                for other_file in other_files[:2]:  # 各関数につき最大2ファイル
                    # 関数のシグネチャと最初の数行を抽出
                    snippet = self._extract_function_snippet(all_files.get(other_file, ''), func_name)
                    if snippet:
                        lines.append(f"     {other_file}:")
                        lines.append(f"     ```")
                        for snippet_line in snippet.split('\n')[:4]:  # 最大4行
                            lines.append(f"     {snippet_line}")
                        lines.append(f"     ```")
                    else:
                        lines.append(f"     {other_file}")
            
            lines.append("")
            lines.append("   → **ACTION REQUIRED**: Keep function in ONE file only.")
            lines.append("   → If this file should own it, implement fully here.")
            lines.append("   → If another file owns it, REMOVE the duplicate from this file.")
            lines.append("")
        
        # ========================================
        # 3. 整合性の注意事項
        # ========================================
        lines.append("**3. Consistency Rules:**")
        lines.append("   - If you rename a function/variable, note that other files may need updates")
        lines.append("   - Maintain consistent API patterns across related files")
        
        result = '\n'.join(lines)
        
        # 文字数制限（約500トークン = 約2000文字）
        MAX_CONTEXT_CHARS = 2000
        if len(result) > MAX_CONTEXT_CHARS:
            result = result[:MAX_CONTEXT_CHARS - 50] + "\n... (truncated for token limit)"
            logger.debug(f"[Dependency Context] Truncated context for {filepath}")
        
        return result
    
    # ============================================
    # 🆕 Phase A - P4: 関数名抽出ユーティリティ
    # 目的: 修正前後の関数削除を検出するため
    # ============================================
    
    def _extract_function_names(self, filepath: str, content: str) -> Set[str]:
        """
        ファイルから関数名を抽出（P4: 関数削除検証用）
        
        Args:
            filepath: ファイルパス（拡張子判定用）
            content: ファイル内容
        
        Returns:
            関数名のセット
        """
        import re
        
        is_python = filepath.endswith('.py')
        is_js = any(filepath.endswith(ext) for ext in ['.js', '.jsx', '.ts', '.tsx'])
        
        if not (is_python or is_js):
            return set()
        
        # 検出パターン（_analyze_file_dependenciesと同じ）
        js_func_patterns = [
            r"(?:async\s+)?function\s+(\w+)\s*\(",
            r"(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>",
            r"(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?function",
            r"^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{",  # ES6クラスメソッド
        ]
        
        py_func_patterns = [
            r"(?:async\s+)?def\s+(\w+)\s*\(",
        ]
        
        # 除外リスト（予約語・一般的な名前）
        # _analyze_file_dependenciesと同じリストを使用
        common_names = {
            # JavaScript/Python予約語
            'for', 'if', 'else', 'while', 'switch', 'case', 'default',
            'try', 'catch', 'finally', 'throw', 'with', 'do',
            'return', 'break', 'continue', 'new', 'this', 'super',
            'class', 'extends', 'import', 'export', 'from', 'as',
            'async', 'await', 'yield', 'typeof', 'instanceof', 'in',
            'let', 'const', 'var', 'function',
            # Python特殊メソッド
            '__init__', '__new__', '__del__', '__repr__', '__str__',
            '__call__', '__len__', '__iter__', '__next__', '__getitem__',
            '__setitem__', '__delitem__', '__contains__', '__enter__', '__exit__',
            # 一般的なメソッド名
            'init', 'initialize', 'setup', 'main', 'run', 'start', 'stop',
            'get', 'set', 'update', 'render', 'load', 'save',
            'constructor', 'toString', 'valueOf',
            'setupEventListeners', 'addEventListener', 'removeEventListener',
            'health_check', 'healthCheck', 'ping', 'status',
            'handleClick', 'handleChange', 'handleSubmit', 'handleError',
            'onClick', 'onChange', 'onSubmit', 'onLoad', 'onError',
            'componentDidMount', 'componentWillUnmount', 'componentDidUpdate',
            'useEffect', 'useState', 'useCallback', 'useMemo',
            'fetch', 'parse', 'format', 'validate', 'process',
            'create', 'delete', 'remove', 'add', 'find', 'filter',
            'show', 'hide', 'toggle', 'enable', 'disable',
            'open', 'close', 'reset', 'clear', 'refresh',
            'connect', 'disconnect', 'bind', 'unbind', 'attach', 'detach',
            'destroy', 'dispose', 'cleanup', 'teardown',
        }
        
        patterns = py_func_patterns if is_python else js_func_patterns
        functions = set()
        
        for pattern in patterns:
            matches = re.findall(pattern, content, re.MULTILINE)
            functions.update(m for m in matches if m not in common_names and len(m) > 2)
        
        return functions
    
    # ============================================
    # 🆕 Phase A - P4/P5: 修正品質検証
    # 目的: 膨張・関数削除を検出して低品質な修正を却下
    # 研究根拠: LLM-Meta-SR (arXiv:2505.18602), 
    #           Code Inefficiencies Taxonomy (arXiv:2503.06327)
    # ============================================
    
    def _validate_fix_quality(
        self,
        filepath: str,
        original_content: str,
        fixed_content: str
    ) -> Tuple[bool, str]:
        """
        修正後のコード品質を検証（Phase A: P4関数削除検証 + P5膨張検知）
        
        検証項目:
        1. 過度な縮小（50%以下）の検出 [既存]
        2. 過度な膨張（150%以上）の検出 [P5]
        3. 関数削除の検出 [P4]
        
        Args:
            filepath: ファイルパス
            original_content: 修正前のコード
            fixed_content: 修正後のコード
        
        Returns:
            Tuple[bool, str]: (検証合格, 不合格理由)
        """
        original_lines = len(original_content.strip().split('\n'))
        fixed_lines = len(fixed_content.strip().split('\n'))
        
        # 空ファイルは検証スキップ
        if original_lines == 0:
            return True, ""
        
        # ========================================
        # 検証1: 過度な縮小（既存ロジック）
        # ========================================
        MIN_RATIO = 0.5  # 50%以下に縮小したら却下
        if fixed_lines < original_lines * MIN_RATIO:
            reason = f"Code shrunk too much: {original_lines} -> {fixed_lines} lines ({fixed_lines/original_lines:.1%})"
            logger.debug(f"[Fix Quality] {filepath}: {reason}")
            return False, reason
        
        # ========================================
        # 検証2: 過度な膨張 [P5]
        # test8データ: 158→742行(4.7倍)が問題だった
        # 閾値1.5倍は十分に緩い
        # ========================================
        MAX_RATIO = 1.5  # 150%以上に膨張したら却下
        MIN_LINES_FOR_BLOAT_CHECK = 50  # 50行以上のファイルのみチェック
        
        if original_lines >= MIN_LINES_FOR_BLOAT_CHECK:
            if fixed_lines > original_lines * MAX_RATIO:
                reason = f"Code bloat detected: {original_lines} -> {fixed_lines} lines ({fixed_lines/original_lines:.1%})"
                logger.debug(f"[Fix Quality] {filepath}: {reason}")
                return False, reason
        
        # ========================================
        # 検証3: 関数削除 [P4改 - 純減判定方式]
        # 純減（削除数 > 追加数）は却下
        # 同数の削除・追加はリネームとして警告のみ
        # test22バグ: search_tracks削除で起動不可 → 純減判定で防止
        # ========================================
        original_funcs = self._extract_function_names(filepath, original_content)
        fixed_funcs = self._extract_function_names(filepath, fixed_content)
        
        deleted_funcs = original_funcs - fixed_funcs
        added_funcs = fixed_funcs - original_funcs
        
        if deleted_funcs:
            net_deletion = len(deleted_funcs) - len(added_funcs)
            if net_deletion > 0:
                # 関数の純減 → 却下
                func_list = ', '.join(sorted(deleted_funcs))
                reason = f"Net function loss ({net_deletion}): {func_list}"
                logger.debug(f"[Fix Quality] {filepath}: {reason}")
                return False, reason
            else:
                # 削除あるが追加で相殺 → リネームの可能性、警告のみ
                # 専門家推奨: Destructive Swap対策として詳細ログを出力
                logger.warning(f"⚠️ [Fix Quality] Structural Change Detected in {filepath}")
                logger.warning(f"  - DELETED: {deleted_funcs}")
                logger.warning(f"  - ADDED  : {added_funcs}")
        
        # 全検証合格
        return True, ""
    
    def _fix_comprehensive_issues(
        self,
        all_files: Dict[str, str],
        issues: List[Dict[str, Any]],
        goal: str,
        attempt_tracker: Optional['SemiAutoEngine.FixAttemptTracker'] = None,
        rejected_files: Optional[Set[str]] = None,
        round_number: int = 0  # 🆕 案3: ラウンド番号（0=ラウンド1, 1=ラウンド2, ...）
    ) -> Optional[Dict[str, str]]:
        """
        Fix detected issues (per-file processing + attempt tracking)
        
        Improvement: Instead of sending all issues to LLM at once, split by file
        and fix individually to improve LLM accuracy.
        
        After fixing, performs runtime import validation.
        If import fails, reverts the fix for the failing file.
        
        Args:
            all_files: Generated files
            issues: List of detected issues
            goal: Original goal
            attempt_tracker: Fix attempt tracker (optional)
            rejected_files: Set of files rejected by P4/P5 (D1: skip these files)
            round_number: Current round number (0-indexed) for progressive expansion
        
        Returns:
            Fixed files (None on failure)
        """
        if not issues or not self.llm_manager:
            return None
        
        # ========================================
        # Step 1: Group issues by file
        # ========================================
        issues_by_file: Dict[str, List[Dict[str, Any]]] = {}
        for issue in issues:
            filepath = issue.get('file')
            if filepath and filepath in all_files:
                if filepath not in issues_by_file:
                    issues_by_file[filepath] = []
                issues_by_file[filepath].append(issue)
        
        if not issues_by_file:
            logger.debug("[Comprehensive Review] No files to fix")
            return None
        
        # 🆕 案3: ラウンド進行拡大（Progressive Expansion）
        # 根拠: 論文知見「2-3ラウンドで効果頭打ち」
        #       → ラウンド1で高issue優先、ラウンド2以降で全ファイルカバー
        # ラウンド1: 5ファイル（最も問題の多いファイルに集中）
        # ラウンド2: 15ファイル（中間レベルのカバレッジ）
        # ラウンド3+: 30ファイル（全ファイル対象）
        if round_number == 0:
            MAX_FIX_FILES_LIMIT = 5
        elif round_number == 1:
            MAX_FIX_FILES_LIMIT = 15
        else:
            MAX_FIX_FILES_LIMIT = 30  # 安全弁（大規模プロジェクト対応）
        
        original_file_count = len(issues_by_file)
        if original_file_count > MAX_FIX_FILES_LIMIT:
            # Issue数が多い順にソートして上位N件のみ
            sorted_files = sorted(issues_by_file.items(), key=lambda x: len(x[1]), reverse=True)
            issues_by_file = dict(sorted_files[:MAX_FIX_FILES_LIMIT])
            skipped_count = original_file_count - MAX_FIX_FILES_LIMIT
            logger.debug(f"[Comprehensive Review] Round {round_number + 1}: Limiting to top {MAX_FIX_FILES_LIMIT} files (skipped {skipped_count} files)")
        
        logger.debug(f"[Comprehensive Review] Fixing {len(issues_by_file)} file(s) individually...")
        
        # Create tracker if not provided (backward compatibility)
        if attempt_tracker is None:
            attempt_tracker = self.FixAttemptTracker(max_attempts=2)
        
        # ========================================
        # 🆕 Step 1.5: 依存関係分析
        # 研究根拠: AlphaCodium, Aider Benchmarks
        # ========================================
        dependency_info = self._analyze_file_dependencies(all_files)
        
        # ========================================
        # Step 2: Fix each file individually (strategy changes based on attempt count)
        # ========================================
        updated_files = all_files.copy()
        actually_fixed_files = set()  # 🆕 実際に修正されたファイルを追跡
        total_fixed = 0
        
        for filepath, file_issues in issues_by_file.items():
            # 🆕 D1: P4/P5で却下されたファイルはスキップ
            if rejected_files and filepath in rejected_files:
                logger.debug(f"[Comprehensive Review] {filepath}: Skipping (previously rejected by P4/P5)")
                continue
            
            # Check attempt count
            attempt_count = attempt_tracker.get_attempt_count(filepath)
            
            # 🆕 依存関係コンテキストを生成
            dep_context = self._extract_dependency_context(
                filepath, all_files, dependency_info.get(filepath, {})
            )
            if dep_context:
                logger.debug(f"[Comprehensive Review] {filepath}: Adding dependency context ({len(dep_context)} chars)")
            
            # ============================================================
            # 🆕 改修: Reviewer/Implementerロール分離
            # ============================================================
            use_design_change = False
            review_reason = ""
            
            if attempt_tracker.needs_design_change(filepath):
                # 3rd+ attempt: Force design change (既存ロジック維持)
                use_design_change = True
                review_reason = "3rd+ attempt"
                logger.debug(f"[Comprehensive Review] {filepath}: DESIGN CHANGE required (attempt {attempt_count + 1})")
            else:
                # 🆕 1-2回目でもReviewerが判断
                review_decision, review_reason = self._review_before_fix(
                    filepath=filepath,
                    content=all_files[filepath],
                    issues=file_issues,
                    all_files=all_files,
                    goal=goal
                )
                use_design_change = (review_decision == "DESIGN_CHANGE")
                
                if use_design_change:
                    logger.debug(f"[Comprehensive Review] {filepath}: DESIGN CHANGE (Reviewer: {review_reason})")
                else:
                    logger.debug(f"[Comprehensive Review] {filepath}: MINIMAL CHANGE approved (Reviewer: {review_reason})")
            
            # 実装フェーズ（Implementerロール）
            if use_design_change:
                fixed_content = self._fix_with_design_change(
                    filepath=filepath,
                    content=all_files[filepath],
                    issues=file_issues,
                    goal=goal,
                    previous_issues=attempt_tracker.get_previous_issues(filepath),
                    dependency_context=dep_context
                )
                
                # ============================================================
                # DESIGN CHANGE v2.1: Abort戻り値の処理
                # ============================================================
                if fixed_content and isinstance(fixed_content, str):
                    if fixed_content.startswith("HARD_ABORT:") or fixed_content.startswith("ESCALATE:"):
                        abort_reason = fixed_content.split(":", 1)[1] if ":" in fixed_content else "Unknown"
                        logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: {fixed_content.split(':')[0]} - {abort_reason}")
                        # HARD_ABORT/ESCALATEの場合は修正をスキップ
                        if rejected_files is not None:
                            rejected_files.add(filepath)
                        continue
                    elif fixed_content.startswith("SOFT_ABORT:"):
                        abort_reason = fixed_content.split(":", 1)[1] if ":" in fixed_content else "Unknown"
                        logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: SOFT_ABORT - {abort_reason}")
                        
                        # ============================================================
                        # 🆕 v2.1: SOFT_ABORT後の残存問題を再評価
                        # 残った問題がMINIMAL_CHANGEで対応可能なら降格して再試行
                        # ============================================================
                        if file_issues:
                            re_review_decision, re_review_reason = self._review_before_fix(
                                filepath=filepath,
                                content=all_files[filepath],  # 元のコンテンツ
                                issues=file_issues,
                                all_files=all_files,
                                goal=goal
                            )
                            
                            if re_review_decision == "MINIMAL_CHANGE":
                                logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Demoting to MINIMAL_CHANGE after SOFT_ABORT - {re_review_reason}")
                                fixed_content = self._fix_with_invariants(
                                    filepath=filepath,
                                    content=all_files[filepath],
                                    issues=file_issues,
                                    goal=goal,
                                    dependency_context=dep_context
                                )
                                # MINIMAL_CHANGEの結果を検証
                                if fixed_content and isinstance(fixed_content, str) and fixed_content.startswith("ESCALATE:"):
                                    # ESCALATEした場合、既にDESIGN CHANGEで失敗しているのでスキップ
                                    logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: MINIMAL_CHANGE escalated, but DESIGN_CHANGE already failed, skipping")
                                    continue
                                elif not fixed_content:
                                    logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: MINIMAL_CHANGE returned no content, skipping")
                                    continue
                                # 有効な修正があれば後続の処理へ流す（continueしない）
                            else:
                                logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Re-review still requires DESIGN_CHANGE ({re_review_reason}), skipping")
                                continue
                        else:
                            continue
            else:
                fixed_content = self._fix_with_invariants(
                    filepath=filepath,
                    content=all_files[filepath],
                    issues=file_issues,
                    goal=goal,
                    dependency_context=dep_context
                )
                
                # 🆕 エスカレーション検出: MINIMAL CHANGEが証明できなかった場合
                if fixed_content and isinstance(fixed_content, str) and fixed_content.startswith("ESCALATE:"):
                    escalate_reason = fixed_content[9:]  # "ESCALATE:"を除去
                    logger.debug(f"[Comprehensive Review] {filepath}: Proof failed, escalating to DESIGN CHANGE - {escalate_reason}")
                    
                    # DESIGN CHANGEで再試行
                    fixed_content = self._fix_with_design_change(
                        filepath=filepath,
                        content=all_files[filepath],
                        issues=file_issues,
                        goal=goal,
                        previous_issues=attempt_tracker.get_previous_issues(filepath),
                        dependency_context=dep_context
                    )
                    
                    # DESIGN CHANGE v2.1: 再試行後のAbort処理
                    if fixed_content and isinstance(fixed_content, str):
                        if fixed_content.startswith("HARD_ABORT:") or fixed_content.startswith("ESCALATE:") or fixed_content.startswith("SOFT_ABORT:"):
                            abort_type = fixed_content.split(":")[0]
                            abort_reason = fixed_content.split(":", 1)[1] if ":" in fixed_content else "Unknown"
                            logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: {abort_type} after escalation - {abort_reason}")
                            if rejected_files is not None:
                                rejected_files.add(filepath)
                            continue
            
            # Abort系の戻り値を除外してから処理
            is_abort_response = (
                fixed_content and 
                isinstance(fixed_content, str) and 
                (fixed_content.startswith("ESCALATE:") or 
                 fixed_content.startswith("HARD_ABORT:") or 
                 fixed_content.startswith("SOFT_ABORT:"))
            )
            
            if fixed_content and not is_abort_response:
                # 🆕 Diff Sanitizer: Issue範囲外の変更を削除（test19追加）
                # `_fix_with_invariants`や`_fix_with_design_change`のフォールバック出力にも適用
                original_content = all_files[filepath]
                sanitized_content, removed_count = self._sanitize_diff(
                    original_content, fixed_content, file_issues, filepath
                )
                
                # サニタイズ後の構文チェック
                is_syntax_valid, syntax_error = self._validate_python_syntax(sanitized_content, filepath)
                if not is_syntax_valid:
                    logger.debug(f"[Diff Sanitizer] Post-sanitize syntax error in {filepath}: {syntax_error}")
                    # サニタイズで壊れた場合、サニタイズ前のコードを使用
                    sanitized_content = fixed_content
                    logger.debug(f"[Diff Sanitizer] Falling back to unsanitized code for {filepath}")
                
                # サニタイズ後のコードを使用
                fixed_content = sanitized_content
                
                # 🆕 Phase A: 修正品質の検証（P4: 関数削除、P5: 膨張検知）
                is_valid, reject_reason = self._validate_fix_quality(
                    filepath, all_files[filepath], fixed_content
                )
                
                if is_valid:
                    # Record issue IDs only when fix is actually applied
                    issue_ids = [f"{i.get('type', '?')}:{i.get('description', '')[:30]}" for i in file_issues]
                    attempt_tracker.record_attempt(filepath, issue_ids)
                    
                    updated_files[filepath] = fixed_content
                    actually_fixed_files.add(filepath)
                    total_fixed += 1
                    logger.debug(f"[Comprehensive Review] ✅ Fixed {filepath}")
                else:
                    # 🆕 品質検証に失敗した場合は却下
                    logger.debug(f"[Comprehensive Review] ⚠ Rejected fix for {filepath}: {reject_reason}")
                    # 🆕 D1: 却下されたファイルを記録（以降のラウンドでスキップ）
                    if rejected_files is not None:
                        rejected_files.add(filepath)
            else:
                logger.debug(f"[Comprehensive Review] ⚠ Could not fix {filepath}")
        
        if total_fixed > 0:
            logger.debug(f"[Comprehensive Review] Successfully fixed {total_fixed}/{len(issues_by_file)} file(s)")
            
            # 🆕 実行時検証: 修正後のコードがインポート可能か確認
            import_success, import_error, failing_file = self._validate_runtime_import(updated_files)
            
            if import_success:
                # 成功またはEXTERNAL_PACKAGE（外部パッケージ不足はコードのバグではない）
                if import_error and import_error.startswith("EXTERNAL_PACKAGE:"):
                    logger.debug(f"[Comprehensive Review] Runtime import OK (external package required)")
                else:
                    logger.debug("[Comprehensive Review] ✅ Runtime import validation passed")
            else:
                # 実行時エラーが発生した場合
                logger.debug(f"[Comprehensive Review] ⚠ Runtime import failed after fix: {import_error}")
                
                if failing_file:
                    logger.debug(f"[Comprehensive Review] ⚠ Failing file: {failing_file}")
                    
                    # 修正されたファイルの場合のみrevert
                    if failing_file in actually_fixed_files:
                        logger.debug(f"[Comprehensive Review] Reverting fix for {failing_file}")
                        updated_files[failing_file] = all_files[failing_file]  # 元に戻す
                        actually_fixed_files.discard(failing_file)
                        total_fixed -= 1
                        
                        if total_fixed <= 0:
                            logger.debug("[Comprehensive Review] All fixes reverted due to runtime errors")
                            return None
                else:
                    # failing_fileが特定できない場合（セットアップエラー等）
                    logger.debug("[Comprehensive Review] ⚠ Could not identify failing file, continuing without revert")
            
            return updated_files
        else:
            logger.debug("[Comprehensive Review] No files were successfully fixed")
            return None
    
    def _extract_target_line_ranges(
        self,
        issues: List[Dict[str, Any]],
        margin: int = None  # デフォルトはDIFF_SANITIZER_MARGINを使用
    ) -> str:
        """
        Issue情報から対象行範囲を抽出（SCOPE ENFORCEMENT用）
        
        Args:
            issues: Issue情報のリスト
            margin: 許可するマージン（前後N行）
            
        Returns:
            "L10-L16, L25-L31" 形式の文字列
        """
        # デフォルト値を定数から取得
        if margin is None:
            margin = DIFF_SANITIZER_MARGIN
            
        ranges = []
        for issue in issues:
            line = issue.get('line')
            if line:
                start = max(1, line - margin)
                end = line + margin
                ranges.append((start, end))
        
        if not ranges:
            return "entire file (no specific line numbers)"
        
        # 重複・隣接範囲をマージ
        ranges.sort(key=lambda x: x[0])
        merged = []
        for start, end in ranges:
            if merged and start <= merged[-1][1] + 1:
                # 隣接または重複 → マージ
                merged[-1] = (merged[-1][0], max(merged[-1][1], end))
            else:
                merged.append((start, end))
        
        return ", ".join(f"L{s}-L{e}" for s, e in merged)
    
    def _sanitize_diff(
        self,
        original_content: str,
        fixed_content: str,
        issues: List[Dict[str, Any]],
        filepath: str,
        margin: int = None  # デフォルトはDIFF_SANITIZER_MARGINを使用
    ) -> Tuple[str, int]:
        """
        🆕 Diff Sanitizer: Issue外の変更を機械的に削除
        
        LLMが勝手に行った「リファクタリング」や「お節介修正」を
        機械的に削ぎ落とし、Issue範囲内の変更のみを適用する。
        
        Args:
            original_content: 修正前のコード
            fixed_content: LLMが出力した修正後のコード
            issues: Issue情報（許可する行範囲の計算に使用）
            filepath: ファイルパス（ログ用）
            margin: 許可するマージン（前後N行）
            
        Returns:
            Tuple[str, int]: (サニタイズ後のコード, 削除された変更数)
        """
        # デフォルト値を定数から取得
        if margin is None:
            margin = DIFF_SANITIZER_MARGIN
            
        try:
            import difflib
            
            original_lines = original_content.splitlines(keepends=True)
            fixed_lines = fixed_content.splitlines(keepends=True)
            
            # 最後の行に改行がない場合の対応
            if original_lines and not original_lines[-1].endswith('\n'):
                original_lines[-1] += '\n'
            if fixed_lines and not fixed_lines[-1].endswith('\n'):
                fixed_lines[-1] += '\n'
            
            # 許可された行番号のセットを構築（1-indexed）
            allowed_lines = set()
            for issue in issues:
                line = issue.get('line')
                if line:
                    for l in range(max(1, line - margin), line + margin + 1):
                        allowed_lines.add(l)
            
            # 行番号の指定がない場合、全行を許可
            if not allowed_lines:
                return fixed_content, 0
            
            # 行ごとに差分を比較
            result_lines = []
            removed_count = 0
            matcher = difflib.SequenceMatcher(None, original_lines, fixed_lines)
            
            for tag, i1, i2, j1, j2 in matcher.get_opcodes():
                if tag == 'equal':
                    # 変更なし → そのまま追加
                    result_lines.extend(original_lines[i1:i2])
                elif tag == 'replace':
                    # 置換: 影響を受ける元の行が許可範囲内かチェック
                    affected_lines = set(range(i1 + 1, i2 + 1))  # 1-indexed
                    if affected_lines & allowed_lines:
                        # 許可範囲内 → 修正を適用
                        result_lines.extend(fixed_lines[j1:j2])
                    else:
                        # 許可範囲外 → 元に戻す
                        result_lines.extend(original_lines[i1:i2])
                        removed_count += (i2 - i1)
                elif tag == 'delete':
                    # 削除: 削除される行が許可範囲内かチェック
                    affected_lines = set(range(i1 + 1, i2 + 1))  # 1-indexed
                    if affected_lines & allowed_lines:
                        # 許可範囲内 → 削除を適用（何も追加しない）
                        pass
                    else:
                        # 許可範囲外 → 元に戻す
                        result_lines.extend(original_lines[i1:i2])
                        removed_count += (i2 - i1)
                elif tag == 'insert':
                    # 挿入: 挿入位置（元ファイルのインデックス）が許可範囲内かチェック
                    # 専門家推奨: 「挿入された中身」ではなく「挿入された場所」を評価
                    insert_position = i1 + 1  # 1-indexed (挿入は i1 の後に行われる)
                    # 隣接する行が許可範囲内であれば、挿入を許可
                    adjacent_allowed = (insert_position in allowed_lines or 
                                       (insert_position - 1) in allowed_lines or
                                       (insert_position + 1) in allowed_lines)
                    if adjacent_allowed:
                        # 許可範囲内 → 挿入を適用
                        result_lines.extend(fixed_lines[j1:j2])
                    else:
                        # 許可範囲外 → 挿入を無視
                        removed_count += (j2 - j1)
            
            sanitized = ''.join(result_lines)
            
            # ログ出力
            if removed_count > 0:
                logger.debug(f"[Diff Sanitizer] {filepath}: Removed {removed_count} unauthorized change(s) outside target ranges")
            else:
                logger.debug(f"[Diff Sanitizer] {filepath}: All changes within allowed scope")
            
            return sanitized, removed_count
            
        except Exception as e:
            # エラー時はサニタイズをスキップし、元のfixed_contentを返す
            logger.debug(f"[Diff Sanitizer] Error processing {filepath}: {e} - Skipping sanitization")
            return fixed_content, 0
    
    def _validate_python_syntax(
        self,
        content: str,
        filepath: str
    ) -> Tuple[bool, str]:
        """
        🆕 Pythonコードの構文を検証
        
        Args:
            content: 検証するコード
            filepath: ファイルパス
            
        Returns:
            Tuple[bool, str]: (有効かどうか, エラーメッセージ)
        """
        if not filepath.endswith('.py'):
            return True, ""
        
        try:
            import ast
            ast.parse(content)
            return True, ""
        except SyntaxError as e:
            error_msg = f"Line {e.lineno}: {e.msg}"
            logger.debug(f"[Syntax Check] ❌ {filepath}: {error_msg}")
            return False, error_msg
        except Exception as e:
            # ast.parse()の予期せぬエラー（例: contentがNone等）
            logger.debug(f"[Syntax Check] ⚠ Unexpected error for {filepath}: {e}")
            return True, ""  # 検証をスキップして続行
    
    def _fix_single_file_issues(
        self,
        filepath: str,
        content: str,
        issues: List[Dict[str, Any]],
        goal: str
    ) -> Optional[str]:
        """
        単一ファイルの問題を修正
        
        Args:
            filepath: ファイルパス
            content: ファイル内容
            issues: このファイルの問題リスト
            goal: 元のゴール
        
        Returns:
            修正後のコード（失敗時はNone）
        """
        if not issues or not self.llm_manager:
            return None
        
        # 問題情報を整形
        issues_text = ""
        for i, issue in enumerate(issues, 1):
            issues_text += f"{i}. [{issue.get('type', '?')}]"
            if issue.get('line'):
                issues_text += f" Line {issue['line']}"
            issues_text += f"\n   Problem: {issue.get('description', 'No description')}\n"
            issues_text += f"   Fix: {issue.get('fix', 'No suggestion')}\n\n"
        
        # フレームワーク制約を構築
        framework_constraint = ""
        if getattr(self, '_no_framework_mode', False):
            framework_constraint = """
⚠️ CRITICAL FRAMEWORK RESTRICTION:
- DO NOT use React, Vue, Angular, or any framework
- DO NOT generate JSX syntax
- YOU MUST generate REAL, FUNCTIONAL vanilla JavaScript code
- The fixed code must work standalone without any framework
"""
        
        # ゴールの切り詰め（4000文字まで許容）
        goal_truncated = goal[:4000] + "..." if len(goal) > 4000 else goal
        
        # 🆕 SCOPE ENFORCEMENT: 対象行範囲を抽出
        target_line_ranges = self._extract_target_line_ranges(issues)
        
        # 🆕 SCOPE ENFORCEMENT プロンプト（最上段に配置）
        scope_enforcement = f"""
### ⚠️ SCOPE ENFORCEMENT (HIGHEST PRIORITY) ⚠️

Your edit scope is STRICTLY LIMITED to the following line ranges: {target_line_ranges}

**ABSOLUTE RULES - VIOLATION = IMMEDIATE REJECTION:**
1. You are NOT an optimizer. You are a surgery robot.
2. You may ONLY modify lines within ±5 lines of each issue location.
3. Every line outside the scope MUST remain byte-identical to the original.
4. If you change a single character outside the scope, the ENTIRE operation FAILS.
5. DO NOT refactor, optimize, or "improve" any code outside the issue locations.
6. DO NOT rename variables, reformat code, or reorganize imports outside the scope.
7. DO NOT change indentation unless necessary for the fix.
8. DO NOT add or remove comments unrelated to the issues.

"""
        
        prompt = f"""Fix the following issues in this single file.
{scope_enforcement}
{framework_constraint}
**File:** {filepath}

**Original Goal (for context):**
{goal_truncated}

**Issues to Fix ({len(issues)} issue(s)):**
{issues_text}

**Current Code:**
```
{content}
```

**Instructions:**
1. Fix ALL listed issues completely
2. For TODO/unimplemented features: Implement the actual functionality
3. For error handling: Add proper try-catch with user feedback
4. For state management: Ensure proper initialization and reset
5. For save/load: Ensure data is properly serialized and restored
6. For navigation: Add back buttons or proper flow
7. For library API misuse: Fix according to library documentation (e.g., Marshmallow: remove required=True when using load_default)
8. For circular imports: Use lazy imports, move imports inside functions, or restructure modules
9. For security issues: Use parameterized queries, escape user input, hash passwords, use environment variables for secrets
10. For resource leaks: Use context managers (with statement), ensure close() is called in finally blocks
11. For type mismatches: Add Optional[] to return type or ensure consistent return values
12. For hardcoded paths: Use os.path.join(), pathlib, or environment variables
13. For debug code: Remove all print/console.log/debugger statements (except intentional logging)
14. For external dependencies: Add timeout parameter, wrap in try-catch, add retry logic or fallback
15. Do NOT add new TODO comments
16. Do NOT remove existing functionality
17. Maintain the existing code style

**Output Format:**
Return the complete fixed code wrapped in:
<file path='{filepath}'>
complete fixed code here
</file>"""

        try:
            # ファイルサイズに応じてmax_tokensを調整
            content_lines = len(content.split('\n'))
            max_tokens = 8000 if content_lines < 300 else 12000 if content_lines < 500 else 16000
            
            logger.debug(f"[Comprehensive Review] Sending fix request for {filepath} ({len(issues)} issues, {content_lines} lines, max_tokens={max_tokens})...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code quality expert. Fix the issues in this single file. Return only the complete fixed code.",
                max_tokens=max_tokens
            )
            
            logger.debug(f"[Comprehensive Review] LLM response received for {filepath}")
            
            if hasattr(response, 'content'):
                response_text = response.content
            else:
                response_text = str(response)
            
            # ファイル抽出
            fixed_files = self._parse_llm_response(response_text)
            
            # 修正コードを取得
            fixed_code = None
            if fixed_files:
                logger.debug(f"[Comprehensive Review] Extracted {len(fixed_files)} file(s) from LLM response")
                
                # 対象ファイルの内容を探す
                for fpath, code in fixed_files.items():
                    # パスの正規化（バックスラッシュ対応）
                    normalized_fpath = fpath.replace('\\', '/')
                    normalized_filepath = filepath.replace('\\', '/')
                    if normalized_fpath == normalized_filepath or fpath == filepath:
                        logger.debug(f"[Comprehensive Review] Found exact match for {filepath}")
                        fixed_code = code
                        break
                
                # ファイル名だけで一致を試みる
                if not fixed_code:
                    target_filename = filepath.split('/')[-1].split('\\')[-1]
                    for fpath, code in fixed_files.items():
                        if fpath.endswith(target_filename):
                            logger.debug(f"[Comprehensive Review] Found filename match: {fpath} -> {filepath}")
                            fixed_code = code
                            break
                
                # 1つしかファイルがなければそれを使う
                if not fixed_code and len(fixed_files) == 1:
                    logger.debug(f"[Comprehensive Review] Using single file from response for {filepath}")
                    fixed_code = list(fixed_files.values())[0]
                
                if not fixed_code:
                    logger.debug(f"[Comprehensive Review] Could not match response to {filepath}, available: {list(fixed_files.keys())}")
            else:
                logger.debug(f"[Comprehensive Review] No files extracted from LLM response for {filepath}")
            
            # 🆕 修正コードが取得できた場合、サニタイズと構文チェックを実行
            if fixed_code:
                # Step 1: 構文チェック（Pythonファイルのみ）
                is_valid, error_msg = self._validate_python_syntax(fixed_code, filepath)
                if not is_valid:
                    logger.debug(f"[Syntax Check] ❌ {filepath}: {error_msg} - Rejecting fix")
                    return None
                
                # Step 2: Diff Sanitizer（Issue範囲外の変更を削除）
                sanitized_code, removed_count = self._sanitize_diff(
                    content, fixed_code, issues, filepath
                )
                
                # Step 3: サニタイズ後の構文チェック
                is_valid, error_msg = self._validate_python_syntax(sanitized_code, filepath)
                if not is_valid:
                    logger.debug(f"[Syntax Check] ❌ Post-sanitize syntax error in {filepath}: {error_msg}")
                    # サニタイズで壊れた場合、サニタイズ前のコードを返す
                    logger.debug(f"[Diff Sanitizer] Falling back to unsanitized code for {filepath}")
                    return fixed_code
                
                return sanitized_code
            
            return None
            
        except Exception as e:
            logger.debug(f"[Comprehensive Review] Fix error for {filepath}: {e}")
            return None

    # ============================================================
    # 🆕 改修: Reviewer/Implementerロール分離
    # ============================================================
    def _review_before_fix(
        self,
        filepath: str,
        content: str,
        issues: List[Dict[str, Any]],
        all_files: Dict[str, str],
        goal: str
    ) -> Tuple[str, str]:
        """
        修正アプローチのレビュー判断（Reviewerロール）
        
        LLMに「コードを書かせず、判断のみさせる」ことで、
        MINIMAL CHANGE vs DESIGN CHANGE の適切な選択を行う。
        
        根拠: LLMは「判断」より「正当化」が得意
              → 判断させるのではなく、証明を要求する
        
        Args:
            filepath: 対象ファイルパス
            content: ファイル内容
            issues: このファイルのIssueリスト
            all_files: 全ファイル（依存関係確認用）
            goal: 元のゴール
        
        Returns:
            Tuple[str, str]: (decision, reason)
                - decision: "MINIMAL_CHANGE" or "DESIGN_CHANGE"
                - reason: 判断理由
        """
        if not issues or not self.llm_manager:
            return ("MINIMAL_CHANGE", "No issues or LLM unavailable")
        
        # Issue情報をフォーマット
        issues_text = ""
        for i, issue in enumerate(issues, 1):
            issues_text += f"{i}. [{issue.get('type', '?')}] {issue.get('description', 'No description')[:100]}\n"
        
        # 依存ファイルを列挙
        dependent_files = []
        filepath_basename = filepath.split('/')[-1].split('\\')[-1]
        for other_path, other_content in all_files.items():
            if other_path != filepath:
                # このファイルをインポートしているか確認
                if filepath_basename.replace('.py', '') in other_content:
                    dependent_files.append(other_path)
        
        dependent_files_text = ", ".join(dependent_files[:10]) if dependent_files else "None detected"
        content_lines = len(content.split('\n'))
        
        prompt = f"""You are a CODE REVIEWER, not an implementer.
Your job is to decide whether MINIMAL CHANGE is appropriate for these issues.
DO NOT write any code in this response. Only provide analysis and decision.

**File under review:** {filepath}
**File size:** {content_lines} lines

**Issues to evaluate:**
{issues_text}

**Files that may depend on this file:**
{dependent_files_text}

**Your task:**
To approve MINIMAL CHANGE, you must PROVE all of the following:

1. LOCALITY PROOF: Can each issue be fixed by modifying ≤5 lines?
   - Count the minimum lines that must change for each issue
   - If total > 5 lines: FAIL

2. ISOLATION PROOF: Will changing these lines affect other files?
   - Check if any dependent files use the functions/variables being changed
   - If cross-file impact exists: FAIL

3. PATTERN PROOF: Is this a one-off bug or a systemic pattern?
   - Could the same bug exist in similar code elsewhere?
   - If systemic pattern: FAIL

**Output format (EXACTLY this structure):**
<review>
<locality_proof>
[PASS or FAIL] - [specific line count and reasoning]
</locality_proof>

<isolation_proof>
[PASS or FAIL] - [list impacted files or "none"]
</isolation_proof>

<pattern_proof>
[PASS or FAIL] - [one-off or systemic, with reasoning]
</pattern_proof>

<decision>[MINIMAL_CHANGE or DESIGN_CHANGE]</decision>
<reason>[One sentence explaining the decision]</reason>
</review>"""
        
        try:
            logger.debug(f"[Review] Evaluating fix approach for {filepath}...")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a senior code reviewer. Evaluate whether minimal changes are safe. DO NOT write any code.",
                max_tokens=2000  # レビューなので少なめでOK
            )
            
            response_text = response.content if hasattr(response, 'content') else str(response)
            
            # 判断結果をパース
            decision_match = re.search(r'<decision>\s*(MINIMAL_CHANGE|DESIGN_CHANGE)\s*</decision>', response_text, re.IGNORECASE)
            reason_match = re.search(r'<reason>([^<]+)</reason>', response_text)
            
            if decision_match:
                decision = decision_match.group(1).upper()
                reason = reason_match.group(1).strip() if reason_match else "No reason provided"
                
                # FAIL判定の数をカウント（安全装置）
                fail_count = response_text.upper().count('FAIL')
                if fail_count > 0 and decision == "MINIMAL_CHANGE":
                    # FAILがあるのにMINIMAL_CHANGEを選んだ場合は矯正
                    logger.debug(f"[Review] Correcting decision: {fail_count} FAILs found but MINIMAL_CHANGE selected")
                    decision = "DESIGN_CHANGE"
                    reason = f"Corrected: {fail_count} proof(s) failed"
                
                logger.debug(f"[Review] {filepath}: {decision} - {reason}")
                return (decision, reason)
            else:
                # パース失敗時はデフォルトでMINIMAL_CHANGE（保守的）
                logger.debug(f"[Review] {filepath}: Could not parse decision, defaulting to MINIMAL_CHANGE")
                return ("MINIMAL_CHANGE", "Parse failed, using default")
                
        except Exception as e:
            logger.debug(f"[Review] Error reviewing {filepath}: {e}")
            return ("MINIMAL_CHANGE", f"Review error: {e}")

    def _fix_with_invariants(
        self,
        filepath: str,
        content: str,
        issues: List[Dict[str, Any]],
        goal: str,
        dependency_context: str = ""  # 🆕 依存関係コンテキスト
    ) -> Optional[str]:
        """
        Minimal-change fix (1st-2nd attempt) - v2: 最小変更原則
        
        根拠: 学術論文の知見
          - LLM4CVE (2025): "make the minimal amount of changes required"
          - Less is More (AAAI2025): "generate patches with minimal modifications"
        
        Args:
            filepath: File path
            content: File content
            issues: List of issues for this file
            goal: Original goal
            dependency_context: Cross-file dependency information (optional)
        
        Returns:
            Fixed code (None on failure)
        """
        if not issues or not self.llm_manager:
            return None
        
        # Format issue information
        issues_text = ""
        for i, issue in enumerate(issues, 1):
            issues_text += f"{i}. [{issue.get('type', '?')}]"
            if issue.get('line'):
                issues_text += f" Line {issue['line']}"
            issues_text += f"\n   Problem: {issue.get('description', 'No description')}\n"
            issues_text += f"   Why bad: {issue.get('fix', 'Unknown')}\n\n"
        
        # Framework constraint
        framework_constraint = ""
        if getattr(self, '_no_framework_mode', False):
            framework_constraint = """
⚠️ FRAMEWORK RESTRICTION: Do NOT use React, Vue, Angular. Use vanilla JavaScript only.
"""
        
        # Truncate goal
        goal_truncated = goal[:3000] + "..." if len(goal) > 3000 else goal
        
        # 🆕 依存関係コンテキストセクション
        dependency_section = ""
        if dependency_context:
            dependency_section = f"""
{dependency_context}

"""
        
        # 🆕 C1: コードサイズ計算（膨張抑制指示用）
        content_lines = len(content.split('\n'))
        max_allowed_lines = int(content_lines * 1.3)  # 130%まで許容
        
        # 🆕 test19: SCOPE ENFORCEMENT（対象行範囲を抽出）
        target_line_ranges = self._extract_target_line_ranges(issues)
        
        scope_enforcement = f"""
### ⚠️ SCOPE ENFORCEMENT (HIGHEST PRIORITY) ⚠️
Your edit scope is STRICTLY LIMITED to: {target_line_ranges}

**ABSOLUTE RULES - VIOLATION = IMMEDIATE REJECTION:**
1. You are NOT an optimizer. You are a surgery robot.
2. You may ONLY modify lines within ±5 lines of each issue location.
3. Every line outside the scope MUST remain byte-identical to the original.
4. If you change a single character outside the scope, the ENTIRE operation FAILS.
5. DO NOT refactor, optimize, or "improve" any code outside the issue locations.
6. DO NOT rename variables, reformat code, or reorganize imports outside the scope.
7. DO NOT change indentation unless necessary for the fix.
8. DO NOT add or remove comments unrelated to the issues.

"""
        
        # 🆕 v2: Minimal Change Fix プロンプト
        # 根拠: 学術論文の知見
        #   - LLM4CVE (2025): "make the minimal amount of changes required"
        #   - Less is More (AAAI2025): "generate patches with minimal modifications"
        #   - CigaR (2024): "maximize trade-off between conciseness and information"
        prompt = f"""**MINIMAL CHANGE FIX** (最小変更修正)
{scope_enforcement}
{dependency_section}
**CORE PRINCIPLE:** Make the SMALLEST change that fixes the issue.
Smaller changes = fewer side effects = no whack-a-mole.
{framework_constraint}

**PROOF REQUIREMENT (MANDATORY):**
Before writing ANY code, you must PROVE all of the following.
If you CANNOT prove ANY of these, you MUST output <escalate> and STOP.

1. LOCALITY PROOF: This bug can be fixed by modifying ≤5 lines in this file only
   → List the exact line numbers that need to change
   → If more than 5 lines needed: FAIL → escalate

2. ISOLATION PROOF: No other file imports, calls, or depends on the lines being changed
   → Check imports/exports affected by your change
   → If cross-file impact exists: FAIL → escalate

3. NON-RECURRENCE PROOF: This exact bug pattern does not exist elsewhere in the codebase
   → Is this a one-off mistake or a systemic pattern?
   → If systemic pattern: FAIL → escalate

**CRITICAL:** If ANY proof fails, output ONLY this and STOP:
<escalate reason="[WHICH proof failed: LOCALITY/ISOLATION/NON-RECURRENCE] - [specific reason]">
DESIGN CHANGE required
</escalate>

DO NOT write any code if escalation is needed.

**FIX RULES (only if all proofs PASS):**
1. Change ONLY the problematic lines - do NOT touch unrelated code
2. Do NOT refactor or reorganize code structure
3. Do NOT add new functions unless absolutely necessary
4. Do NOT change function signatures (breaks callers)
5. Do NOT change import/export names (breaks dependents)
6. Keep the fix as SMALL as possible

**SIZE CONSTRAINT (CRITICAL):**
- Current file: {content_lines} lines
- Maximum allowed: {max_allowed_lines} lines (130% of original)
- FEWER lines = BETTER (if functionality preserved)

**Issues to fix:**
{issues_text}

**Goal context:** {goal_truncated}

**File:** {filepath}
```
{content}
```

**Output Format:**

Option A - If escalation needed (any proof failed):
<escalate reason="[proof that failed] - [specific reason]">
DESIGN CHANGE required
</escalate>

Option B - If all proofs pass:
<proof>
LOCALITY: PASS - Lines [X, Y, Z] will be modified (≤5 lines)
ISOLATION: PASS - No other files depend on these lines
NON-RECURRENCE: PASS - This is a one-off issue, not a pattern
</proof>

<file path='{filepath}'>
COMPLETE fixed code here (entire file content)
</file>"""

        try:
            max_tokens = 8000 if content_lines < 300 else 12000 if content_lines < 500 else 16000
            
            logger.debug(f"[Comprehensive Review] Sending minimal-change fix for {filepath} ({len(issues)} issues, {content_lines} lines)")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a code repair specialist. Make the SMALLEST change that fixes the issue. Return COMPLETE file contents.",
                max_tokens=max_tokens
            )
            
            response_text = response.content if hasattr(response, 'content') else str(response)
            
            # 🆕 改修: エスカレーション検出
            escalate_match = re.search(r'<escalate[^>]*reason="([^"]*)"[^>]*>', response_text, re.IGNORECASE)
            if not escalate_match:
                # 別形式のエスカレーションも検出
                escalate_match = re.search(r'<escalate[^>]*>.*?reason[:\s]*([^<\n]+)', response_text, re.IGNORECASE | re.DOTALL)
            
            if escalate_match:
                escalate_reason = escalate_match.group(1).strip()
                logger.debug(f"[Comprehensive Review] {filepath}: ESCALATED by proof failure - {escalate_reason}")
                # "ESCALATE"文字列を返すことで、呼び出し元でDESIGN CHANGEへ移行
                return "ESCALATE:" + escalate_reason
            
            # 通常のファイル抽出処理
            fixed_files = self._parse_llm_response(response_text)
            
            if fixed_files:
                logger.debug(f"[Comprehensive Review] Extracted {len(fixed_files)} file(s) from minimal-change fix")
                # Path normalization and matching
                normalized_filepath = filepath.replace('\\', '/')
                for fpath, code in fixed_files.items():
                    normalized_fpath = fpath.replace('\\', '/')
                    if normalized_fpath == normalized_filepath or fpath == filepath:
                        return code
                    if fpath.endswith(filepath.split('/')[-1].split('\\')[-1]):
                        return code
                if len(fixed_files) == 1:
                    return list(fixed_files.values())[0]
            
            return None
            
        except Exception as e:
            logger.debug(f"[Comprehensive Review] Minimal-change fix error for {filepath}: {e}")
            return None

    # ============================================================
    # DESIGN CHANGE 制御仕様 v2.0
    # ============================================================
    # コンセプト: 「禁止より指示」「具体的に1つだけ」「差分出力」
    _design_change_hard_abort_counts: Dict[str, int] = {}
    
    def _fix_with_design_change(
        self,
        filepath: str,
        content: str,
        issues: List[Dict[str, Any]],
        goal: str,
        previous_issues: List[str],
        dependency_context: str = ""
    ) -> Optional[str]:
        """
        DESIGN CHANGE with Diff-based Minimal Fix (v2.0)
        
        v2.0変更点:
        - 禁止リストを削除、「やること」を具体的に指示
        - 完全ファイル出力→差分出力に変更
        - WRONG/RIGHTの具体例を提示
        - 成功の定義を「最小限の変更」と明確化
        - トークン数を削減（12000→4000）
        
        Args:
            filepath: File path
            content: File content
            issues: List of issues for this file
            goal: Original goal
            previous_issues: History of previous issues (not used in v2.0)
            dependency_context: Cross-file dependency information
        
        Returns:
            Fixed code, "SOFT_ABORT:reason", or None
        """
        if not issues or not self.llm_manager:
            return None
        
        # Format issue information with numbers
        issues_text = ""
        for i, issue in enumerate(issues, 1):
            issues_text += f"{i}. [{issue.get('type', '?')}] {issue.get('description', 'No description')}\n"
        
        # Framework constraint
        framework_constraint = ""
        if getattr(self, '_no_framework_mode', False):
            framework_constraint = "\n⚠️ FRAMEWORK RESTRICTION: Do NOT use React, Vue, Angular. Use vanilla JavaScript only.\n"
        
        # Dependency section
        dependency_section = f"\n{dependency_context}\n" if dependency_context else ""
        
        # Code metrics (for display only)
        content_lines = len(content.split('\n'))
        
        # 🆕 test19: SCOPE ENFORCEMENT（対象行範囲を抽出）
        target_line_ranges = self._extract_target_line_ranges(issues)
        
        scope_enforcement = f"""
### ⚠️ SCOPE ENFORCEMENT (HIGHEST PRIORITY) ⚠️
Your edit scope is STRICTLY LIMITED to: {target_line_ranges}

**ABSOLUTE RULES - VIOLATION = IMMEDIATE REJECTION:**
1. You are NOT an optimizer. You are a surgery robot.
2. You may ONLY modify lines within ±5 lines of each issue location.
3. Every line outside the scope MUST remain byte-identical to the original.
4. If you change a single character outside the scope, the ENTIRE operation FAILS.
5. DO NOT refactor, optimize, or "improve" any code outside the issue locations.
6. DO NOT rename variables, reformat code, or reorganize imports outside the scope.
7. DO NOT change indentation unless necessary for the fix.
8. DO NOT add or remove comments unrelated to the issues.
"""
        
        # ============================================================
        # DESIGN CHANGE プロンプト v2.0
        # コンセプト: 「禁止より指示」「具体的に1つだけ」「差分出力」
        # ============================================================
        prompt = f"""
{scope_enforcement}
{framework_constraint}
{dependency_section}
===== YOUR TASK =====
Fix the issues below. Output ONLY a unified diff.

===== ISSUES TO FIX =====
{issues_text}

===== SUCCESS DEFINITION =====
Your fix is SUCCESSFUL if:
✓ Each issue is fixed with the MINIMUM possible change
✓ You output ONLY a diff (not the full file)
✓ Your diff is 30 lines or less

Your fix is a FAILURE if:
✗ You changed anything not required by the issues
✗ You output more than the diff
✗ Your diff exceeds 30 lines

A SMALL fix is BETTER than a COMPREHENSIVE fix.

===== WRONG vs RIGHT EXAMPLES =====

Example Issue: "playTrack() does not validate track.audio_url"

❌ WRONG (too much):
```diff
@@ -45,8 +45,15 @@
 playTrack(track) {{
+    // Validate track data before playing
+    if (!track || typeof track !== 'object') {{
+        console.error('Invalid track object:', track);
+        return false;
+    }}
+    if (!track.audio_url || typeof track.audio_url !== 'string') {{
+        console.warn('Missing or invalid audio URL');
+        return false;
+    }}
     this.audio.src = track.audio_url;
```

✅ RIGHT (minimal):
```diff
@@ -45,6 +45,7 @@
 playTrack(track) {{
+    if (!track?.audio_url) return;
     this.audio.src = track.audio_url;
```

The RIGHT example adds 1 line. The WRONG example adds 9 lines.
We want RIGHT.

===== OUTPUT FORMAT =====
<diff>
```diff
[Your unified diff here - ONLY the changed parts]
[Maximum 30 lines]
```
</diff>

If fix requires more than 30 diff lines, output instead:
<abort>
SCOPE_EXCEEDED: Fix requires [N] lines, limit is 30.
Issues that CAN be fixed within limit: [list or "none"]
</abort>

===== FILE CONTENT =====
File: {filepath} ({content_lines} lines)
```
{content}
```"""

        try:
            # v2.1: 差分出力なのでトークン数を削減
            max_tokens = 4000
            
            logger.debug(f"[DESIGN CHANGE v2.1] Sending request for {filepath}")
            
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt="You are a precise code editor. Output ONLY unified diff format. Minimal changes only.",
                max_tokens=max_tokens
            )
            
            response_text = response.content if hasattr(response, 'content') else str(response)
            
            # ============================================================
            # レスポンスパース（v2.1仕様 - 差分形式）
            # ============================================================
            
            # 1. Abort検出（SCOPE_EXCEEDED）
            if '<abort>' in response_text.lower() or 'SCOPE_EXCEEDED' in response_text:
                abort_reason_match = re.search(r'SCOPE_EXCEEDED[:\s]*(.+?)(?:\n|$)', response_text)
                abort_reason = abort_reason_match.group(1).strip() if abort_reason_match else "Scope exceeded"
                logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: ABORT - {abort_reason}")
                return f"SOFT_ABORT:{abort_reason}"
            
            # 2. 差分抽出
            diff_match = re.search(r'<diff>\s*```diff\s*(.*?)\s*```\s*</diff>', response_text, re.DOTALL)
            if not diff_match:
                # <diff>タグなしでも```diff```ブロックを探す
                diff_match = re.search(r'```diff\s*(.*?)\s*```', response_text, re.DOTALL)
            
            if diff_match:
                diff_content = diff_match.group(1).strip()
                diff_lines = diff_content.split('\n')
                
                # 差分行数チェック（30行指示、50行受け入れ - バッファ付き制限 v2.1）
                if len(diff_lines) > 50:
                    logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Diff too large ({len(diff_lines)} lines, limit=50)")
                    return f"SOFT_ABORT:Diff exceeded limit ({len(diff_lines)} lines, limit=50)"
                
                # 差分を元のコードに適用（SIC検証付き）
                try:
                    fixed_content = self._apply_unified_diff(content, diff_content)
                    if fixed_content and fixed_content != content:
                        # ============================================================
                        # SIC: Structural Integrity Check（test19バグ対策）
                        # ============================================================
                        sic_ok, sic_error = self._structural_integrity_check(content, fixed_content, filepath)
                        
                        if not sic_ok:
                            logger.debug(f"[SIC] {filepath}: REJECTED - {sic_error}")
                            
                            # ============================================================
                            # LLMへの自動フィードバック再試行（v2.0: 3者比較方式）
                            # ============================================================
                            retry_content = self._retry_with_sic_feedback(
                                filepath=filepath,
                                original_code=content,      # DNA: 修正前の正しいコード
                                failed_code=fixed_content,  # FAILURE: SICで弾かれたコード
                                issues=issues,
                                sic_error=sic_error,
                                goal=goal
                            )
                            if retry_content:
                                # 再試行結果もSICで検証
                                retry_sic_ok, retry_sic_error = self._structural_integrity_check(content, retry_content, filepath)
                                if retry_sic_ok:
                                    logger.debug(f"[SIC Retry] {filepath}: Retry succeeded - SIC passed")
                                    return retry_content
                                else:
                                    logger.debug(f"[SIC Retry] {filepath}: Retry also failed - {retry_sic_error}")
                            
                            logger.debug(f"[SIC] {filepath}: Rolling back diff, using fallback")
                            # diffを却下してフォールバックへ
                        else:
                            logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Applied diff ({len(diff_lines)} lines) - SIC passed")
                            return fixed_content
                    else:
                        logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Diff application failed or no change")
                except Exception as e:
                    logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Diff parse error: {e}")
            
            # 3. フォールバック: 従来の完全コード抽出
            fixed_files = self._parse_llm_response(response_text)
            
            if fixed_files:
                logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: Fallback - extracted {len(fixed_files)} file(s)")
                normalized_filepath = filepath.replace('\\', '/')
                for fpath, code in fixed_files.items():
                    normalized_fpath = fpath.replace('\\', '/')
                    if normalized_fpath == normalized_filepath or fpath == filepath:
                        return code
                    if fpath.endswith(filepath.split('/')[-1].split('\\')[-1]):
                        return code
                if len(fixed_files) == 1:
                    return list(fixed_files.values())[0]
            
            logger.debug(f"[DESIGN CHANGE v2.1] {filepath}: No valid output extracted")
            return None
            
        except Exception as e:
            logger.debug(f"[DESIGN CHANGE v2.1] Error for {filepath}: {e}")
            return None
    
    def _apply_unified_diff(self, original: str, diff: str) -> Optional[str]:
        """
        Unified diff形式の差分を元のコードに適用する
        
        Args:
            original: 元のコード
            diff: unified diff形式の差分
            
        Returns:
            適用後のコード、または失敗時None
        """
        try:
            original_lines = original.split('\n')
            result_lines = original_lines.copy()
            
            # 差分のハンクを解析
            hunk_pattern = r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@'
            
            diff_lines = diff.split('\n')
            
            # すべてのハンクを先に解析
            hunks = []
            i = 0
            while i < len(diff_lines):
                line = diff_lines[i]
                hunk_match = re.match(hunk_pattern, line)
                if hunk_match:
                    old_start = int(hunk_match.group(1))  # 1-indexed
                    old_count = int(hunk_match.group(2)) if hunk_match.group(2) else 1
                    new_start = int(hunk_match.group(3))  # 1-indexed
                    new_count = int(hunk_match.group(4)) if hunk_match.group(4) else 1
                    
                    i += 1
                    hunk_lines = []
                    
                    # ハンクの内容を収集
                    while i < len(diff_lines) and not diff_lines[i].startswith('@@'):
                        hunk_line = diff_lines[i]
                        # diffの行は ' ', '-', '+' で始まる（または空行）
                        if hunk_line.startswith((' ', '-', '+')) or hunk_line == '':
                            hunk_lines.append(hunk_line)
                        i += 1
                    
                    hunks.append({
                        'old_start': old_start,
                        'old_count': old_count,
                        'new_start': new_start,
                        'new_count': new_count,
                        'lines': hunk_lines
                    })
                else:
                    i += 1
            
            if not hunks:
                logger.debug("[Diff Apply] No hunks found in diff")
                return None
            
            # ハンクを逆順に適用（後ろから適用することでoffset計算が不要）
            hunks.sort(key=lambda h: h['old_start'], reverse=True)
            
            for hunk in hunks:
                old_start = hunk['old_start'] - 1  # 0-indexed
                hunk_lines = hunk['lines']
                
                # このハンクで削除される行と追加される行を分離
                lines_to_delete = 0
                new_lines = []
                
                for hunk_line in hunk_lines:
                    if hunk_line.startswith('-'):
                        lines_to_delete += 1
                    elif hunk_line.startswith('+'):
                        new_lines.append(hunk_line[1:])  # '+' を除去
                    elif hunk_line.startswith(' '):
                        new_lines.append(hunk_line[1:])  # ' ' を除去
                    elif hunk_line == '':
                        # 空のコンテキスト行（元のコードの空行）
                        new_lines.append('')
                
                # 古い行を削除して新しい行を挿入
                # old_start から old_count 行を削除
                old_count = hunk['old_count']
                
                # 範囲チェック
                if old_start < 0 or old_start > len(result_lines):
                    logger.debug(f"[Diff Apply] Invalid hunk start: {old_start + 1} (file has {len(result_lines)} lines)")
                    return None
                
                # 削除と挿入を一度に行う
                end_index = min(old_start + old_count, len(result_lines))
                result_lines[old_start:end_index] = new_lines
            
            result = '\n'.join(result_lines)
            
            # 変更があったか確認
            if result == original:
                logger.debug("[Diff Apply] No changes detected after applying diff")
                return None
            
            return result
            
        except Exception as e:
            logger.debug(f"[Diff Apply] Error applying diff: {e}")
            return None

    # ============================================================
    # Structural Integrity Check (SIC) - test19バグ対策
    # ============================================================
    
    def _get_structural_snapshot(self, code: str, filepath: str) -> Optional[Dict[str, Any]]:
        """
        コードの構造スナップショットを取得
        
        Args:
            code: ソースコード
            filepath: ファイルパス（.pyのみ対象）
            
        Returns:
            構造情報の辞書 または None
        """
        if not filepath.endswith('.py'):
            return None
        
        try:
            tree = ast.parse(code)
        except SyntaxError as e:
            logger.debug(f"[SIC] Syntax error in {filepath}: {e}")
            return None
        
        class_count = 0
        function_count = 0
        class_names = []
        function_names = []
        top_level_names = []
        top_level_typed = set()  # 型ヒント付きのトップレベル変数
        class_attributes = {}
        typed_attributes = {}  # クラス名 -> 型ヒント付き属性のset
        
        for node in tree.body:
            if isinstance(node, ast.ClassDef):
                class_count += 1
                class_names.append(node.name)
                
                # クラス内の属性を収集
                attrs = []
                typed_attrs = set()
                for item in node.body:
                    if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
                        attrs.append(item.target.id)
                        typed_attrs.add(item.target.id)  # 型ヒント付き
                    elif isinstance(item, ast.Assign):
                        for target in item.targets:
                            if isinstance(target, ast.Name):
                                attrs.append(target.id)
                                # Assignは型ヒントなし
                class_attributes[node.name] = attrs
                typed_attributes[node.name] = typed_attrs
                
            elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                function_count += 1
                function_names.append(node.name)
                
            elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
                top_level_names.append(node.target.id)
                top_level_typed.add(node.target.id)  # 型ヒント付き
                
            elif isinstance(node, ast.Assign):
                for target in node.targets:
                    if isinstance(target, ast.Name):
                        top_level_names.append(target.id)
                        # Assignは型ヒントなし
        
        return {
            'class_count': class_count,
            'function_count': function_count,
            'class_names': class_names,
            'function_names': function_names,
            'top_level_names': top_level_names,
            'top_level_typed': top_level_typed,
            'class_attributes': class_attributes,
            'typed_attributes': typed_attributes
        }

    def _structural_integrity_check(
        self,
        original: str,
        modified: str,
        filepath: str
    ) -> Tuple[bool, Optional[str]]:
        """
        Structural Integrity Check (SIC)
        
        diff適用後のコードが構造的に健全かチェック
        
        Args:
            original: 元のコード
            modified: 修正後のコード
            filepath: ファイルパス
            
        Returns:
            (成功, エラーメッセージ)
        """
        # HTML/JSファイル用のチェック
        if filepath.endswith(('.html', '.htm', '.js', '.jsx', '.ts', '.tsx')):
            return self._structural_integrity_check_html_js(original, modified, filepath)
        
        if not filepath.endswith('.py'):
            return (True, None)
        
        before = self._get_structural_snapshot(original, filepath)
        after = self._get_structural_snapshot(modified, filepath)
        
        if before is None:
            logger.debug(f"[SIC] {filepath}: Original code unparseable, skipping")
            return (True, None)
        
        if after is None:
            return (False, "Syntax error after diff application")
        
        # Check 1: Symbol Count
        if after['class_count'] < before['class_count']:
            return (False, f"Class count decreased: {before['class_count']} -> {after['class_count']}")
        
        if after['function_count'] < before['function_count'] - 1:
            return (False, f"Function count significantly decreased: {before['function_count']} -> {after['function_count']}")
        
        # Check 2: Top-level Pollution
        for class_name, attrs in before['class_attributes'].items():
            for attr in attrs:
                if attr in after['top_level_names'] and attr not in before['top_level_names']:
                    return (False, f"Attribute '{attr}' leaked from class '{class_name}' to top-level")
        
        # Check 3: Attribute Drift
        CRITICAL_ATTRS = ['DATABASE_URL', 'CORS_ORIGINS', 'SECRET_KEY', 'API_KEY', 'STATIC_DIR']
        
        for class_name in before['class_names']:
            if class_name in after['class_attributes']:
                before_attrs = set(before['class_attributes'].get(class_name, []))
                after_attrs = set(after['class_attributes'].get(class_name, []))
                
                missing_critical = before_attrs - after_attrs
                for attr in missing_critical:
                    if attr in CRITICAL_ATTRS:
                        return (False, f"Critical attribute '{attr}' missing from class '{class_name}'")
        
        # Check 4: Class Name Preservation
        for class_name in before['class_names']:
            if class_name not in after['class_names']:
                return (False, f"Class '{class_name}' disappeared after diff")
        
        # Check 5: Type Annotation Drift（型ヒントの消失検知）
        # AnnAssign（型付き）がAssign（型なし）に変わっていないか
        for class_name in before['class_names']:
            # クラスが消えた場合はCheck 4で検知されるのでスキップ
            if class_name not in after['class_names']:
                continue
            
            before_typed = before.get('typed_attributes', {}).get(class_name, set())
            after_typed = after.get('typed_attributes', {}).get(class_name, set())
            
            # 元々型ヒント付きだった属性が型なしになっていないか
            lost_typing = before_typed - after_typed
            # ただし、属性自体が消えた場合はCheck 3で検知済みなので除外
            after_attrs = set(after.get('class_attributes', {}).get(class_name, []))
            lost_typing_still_exists = lost_typing & after_attrs
            
            if lost_typing_still_exists:
                return (False, f"Type annotations lost in class '{class_name}': {lost_typing_still_exists}")
        
        logger.debug(f"[SIC] {filepath}: All checks passed")
        return (True, None)
    
    def _structural_integrity_check_html_js(
        self,
        original: str,
        modified: str,
        filepath: str
    ) -> Tuple[bool, Optional[str]]:
        """
        HTML/JavaScript用のStructural Integrity Check
        
        チェック項目:
        1. 括弧のバランス（{}, (), []）
        2. HTMLタグのバランス（<script>, </script>など）
        3. class/function定義の数が減っていないか
        4. 異常な構文パターンの検出
        
        Args:
            original: 元のコード
            modified: 修正後のコード
            filepath: ファイルパス
            
        Returns:
            (成功, エラーメッセージ)
        """
        # Check 1: 括弧のバランス
        def count_brackets(code: str) -> Dict[str, int]:
            # 文字列リテラル内の括弧を除外するための簡易処理
            # 完全ではないが、大きな構造破壊は検出できる
            counts = {'{': 0, '}': 0, '(': 0, ')': 0, '[': 0, ']': 0}
            in_string = False
            string_char = None
            prev_char = ''
            
            for char in code:
                if not in_string:
                    if char in ('"', "'", '`'):
                        in_string = True
                        string_char = char
                    elif char in counts:
                        counts[char] += 1
                else:
                    if char == string_char and prev_char != '\\':
                        in_string = False
                        string_char = None
                prev_char = char
            
            return counts
        
        orig_brackets = count_brackets(original)
        mod_brackets = count_brackets(modified)
        
        # 括弧のバランスチェック
        orig_balance = {
            'braces': orig_brackets['{'] - orig_brackets['}'],
            'parens': orig_brackets['('] - orig_brackets[')'],
            'squares': orig_brackets['['] - orig_brackets[']']
        }
        mod_balance = {
            'braces': mod_brackets['{'] - mod_brackets['}'],
            'parens': mod_brackets['('] - mod_brackets[')'],
            'squares': mod_brackets['['] - mod_brackets[']']
        }
        
        # 修正後にバランスが崩れた場合（元が正常だった場合）
        if mod_balance['braces'] != 0 and orig_balance['braces'] == 0:
            return (False, f"Brace balance broken: {'{' * abs(mod_balance['braces'])} {'missing }' if mod_balance['braces'] > 0 else 'extra }'}")
        if mod_balance['parens'] != 0 and orig_balance['parens'] == 0:
            return (False, f"Parenthesis balance broken: {'(' * abs(mod_balance['parens'])} {'missing )' if mod_balance['parens'] > 0 else 'extra )'}")
        if mod_balance['squares'] != 0 and orig_balance['squares'] == 0:
            return (False, f"Bracket balance broken: {'[' * abs(mod_balance['squares'])} {'missing ]' if mod_balance['squares'] > 0 else 'extra ]'}")
        
        # 🆕 追加: 修正後にバランスが悪化した場合も検出
        if abs(mod_balance['braces']) > abs(orig_balance['braces']):
            return (False, f"Brace balance worsened: {orig_balance['braces']} -> {mod_balance['braces']}")
        if abs(mod_balance['parens']) > abs(orig_balance['parens']):
            return (False, f"Parenthesis balance worsened: {orig_balance['parens']} -> {mod_balance['parens']}")
        if abs(mod_balance['squares']) > abs(orig_balance['squares']):
            return (False, f"Bracket balance worsened: {orig_balance['squares']} -> {mod_balance['squares']}")
        
        # Check 2: class/function定義の数
        def count_definitions(code: str) -> Dict[str, int]:
            # JavaScriptのclass定義
            class_count = len(re.findall(r'\bclass\s+\w+', code))
            # function定義（通常のfunctionと矢印関数は別々にカウント）
            function_count = len(re.findall(r'\bfunction\s+\w+', code))
            # メソッド定義（class内）
            method_count = len(re.findall(r'^\s+\w+\s*\([^)]*\)\s*\{', code, re.MULTILINE))
            
            return {
                'classes': class_count,
                'functions': function_count,
                'methods': method_count
            }
        
        orig_defs = count_definitions(original)
        mod_defs = count_definitions(modified)
        
        # クラス数が減少した場合
        if mod_defs['classes'] < orig_defs['classes']:
            return (False, f"Class count decreased: {orig_defs['classes']} -> {mod_defs['classes']}")
        
        # 関数数が大幅に減少した場合（1つまでは許容）
        if mod_defs['functions'] < orig_defs['functions'] - 1:
            return (False, f"Function count significantly decreased: {orig_defs['functions']} -> {mod_defs['functions']}")
        
        # Check 3: HTMLタグのバランス（HTMLファイルのみ）
        if filepath.endswith(('.html', '.htm')):
            important_tags = ['script', 'style', 'head', 'body', 'html']
            for tag in important_tags:
                orig_open = len(re.findall(rf'<{tag}[\s>]', original, re.IGNORECASE))
                orig_close = len(re.findall(rf'</{tag}>', original, re.IGNORECASE))
                mod_open = len(re.findall(rf'<{tag}[\s>]', modified, re.IGNORECASE))
                mod_close = len(re.findall(rf'</{tag}>', modified, re.IGNORECASE))
                
                # 元々バランスが取れていたのに崩れた場合
                if orig_open == orig_close and mod_open != mod_close:
                    return (False, f"HTML tag <{tag}> balance broken: {mod_open} opens, {mod_close} closes")
        
        # Check 4: 異常な構文パターンの検出
        # メソッド定義の途中に別のコードが混入するパターン
        # 例: update() { ... const x = 1; draw() { ...
        anomaly_pattern = r'\b(update|render|draw|init|constructor)\s*\([^)]*\)\s*\{[^}]*\b(const|let|var)\s+\w+\s*=\s*[^;]+;\s*\n\s*(update|render|draw|init|constructor|class)\s*[\s(]'
        if re.search(anomaly_pattern, modified):
            return (False, "Code structure anomaly detected: method definition appears to be corrupted")
        
        logger.debug(f"[SIC-HTML/JS] {filepath}: All checks passed")
        return (True, None)

    def _retry_with_sic_feedback(
        self,
        filepath: str,
        original_code: str,
        failed_code: str,
        issues: List[Dict[str, Any]],
        sic_error: str,
        goal: str
    ) -> Optional[str]:
        """
        SIC Reject時にLLMにフィードバックして再試行（v2.0: 3者比較方式）
        
        改善点:
        1. 3者比較: Original（DNA） + Failed（FAILURE） + Diagnosis
        2. 復元と修正の分離: 「まずORIGINALに戻せ」指示
        3. 具体的診断: エラータイプ別の詳細例示
        
        Args:
            filepath: 対象ファイルパス
            original_code: 修正前の正しいコード（DNA）
            failed_code: SICで弾かれたコード（FAILURE）
            issues: 修正対象のIssueリスト
            sic_error: SICエラーメッセージ
            goal: 元のゴール
            
        Returns:
            修正後のコード、または失敗時None
        """
        # llm_managerの存在チェック
        if not self.llm_manager:
            logger.debug(f"[SIC Retry v2.0] {filepath}: LLM manager not available")
            return None
        
        logger.debug(f"[SIC Retry v2.0] {filepath}: Initiating 3-way comparison recovery")
        
        # Issue情報を整形
        issues_for_file = [i for i in issues if i.get('file') == filepath]
        issues_text = ""
        for i, issue in enumerate(issues_for_file[:3], 1):
            issues_text += f"{i}. {issue.get('description', 'No description')}\n"
        
        # エラーを構造化
        structured_error = self._parse_sic_error(sic_error)
        
        # 診断メッセージを生成
        diagnosis = self._generate_sic_diagnosis(
            structured_error,
            original_code,
            failed_code
        )
        
        # 3者比較プロンプトを構築
        prompt = f"""=== STRUCTURAL INTEGRITY CHECK (SIC) RECOVERY REQUEST ===

You attempted to fix a file, but your output BROKE the code's structural integrity.
You must RECOVER the structure while still applying the necessary fix.

---

## [DNA: ORIGINAL]
This is the STRUCTURALLY CORRECT code BEFORE your modification.
Your new output MUST preserve this exact structure (classes, indentation, attribute locations).

```
{original_code}
```

---

## [FAILURE: YOUR PREVIOUS OUTPUT]
This is YOUR broken output that failed SIC validation.
DO NOT use this as a template. It has structural defects.

```
{failed_code}
```

---

## [DIAGNOSIS: SIC ERROR]
Why your output was rejected:

**Error Type:** {diagnosis["error_type"]}
**Details:** {diagnosis["details"]}

{diagnosis["examples"]}

---

## [MISSION: SURGICAL RECOVERY]

### PROCEDURE (Follow in order):

1. **REVERT FIRST**
   - Start with [DNA: ORIGINAL] as your base template
   - This code has the CORRECT structural integrity
   - Copy its exact indentation pattern

2. **SURGICAL RE-FIX**
   - Apply ONLY the fix for: {issues_text if issues_text else 'the previously listed issues'}
   - Do NOT touch any other lines
   - Do NOT reorganize, refactor, or "improve" anything else

3. **INDENTATION LOCK**
   - Class attributes MUST remain inside their class (indented with 4 spaces)
   - Top-level code MUST remain at column 0
   - Match [DNA: ORIGINAL] indentation exactly

4. **SCOPE CHECK (Before submitting)**
   - Verify: Are all class attributes still INSIDE their class?
   - Verify: Did any class variables leak to module level?
   - Verify: Are all original classes/functions still present?

### CRITICAL CONSTRAINTS

❌ DO NOT: Remove any existing classes or functions
❌ DO NOT: Move class attributes to module level (outside class)
❌ DO NOT: Change indentation patterns
❌ DO NOT: Add "improvements" not related to the original issue
❌ DO NOT: Fix PEP8/style issues (focus on functionality only)

✅ DO: Preserve exact structure from [DNA: ORIGINAL]
✅ DO: Fix only the specific issue mentioned
✅ DO: Return the complete file content

### OUTPUT FORMAT

You MUST use this exact format (the system parses <file> tags):

<file path="{filepath}">
<your complete corrected code here - no markdown code blocks inside>
</file>
"""

        try:
            response = self.llm_manager.generate_response(
                prompt=prompt,
                system_prompt=(
                    "You are a SURGICAL CODE RECOVERY SPECIALIST. "
                    "Your job is to RESTORE structural integrity while applying minimal fixes. "
                    "NEVER reorganize, refactor, or 'improve' code beyond the specific fix requested. "
                    "Pay EXTREME attention to Python indentation - it determines code structure."
                ),
                max_tokens=8000
            )
            
            response_text = response.content if hasattr(response, 'content') else str(response)
            
            # コード抽出
            fixed_files = self._parse_llm_response(response_text)
            
            if fixed_files:
                for fpath, code in fixed_files.items():
                    if fpath == filepath or fpath.endswith(filepath.split('/')[-1]):
                        logger.debug(f"[SIC Retry v2.0] {filepath}: Extracted recovered code ({len(code)} chars)")
                        return code
                if len(fixed_files) == 1:
                    return list(fixed_files.values())[0]
            
            logger.debug(f"[SIC Retry v2.0] {filepath}: No valid code extracted from recovery attempt")
            return None
            
        except Exception as e:
            logger.debug(f"[SIC Retry v2.0] {filepath}: Recovery failed - {e}")
            return None

    def _parse_sic_error(self, sic_error: str) -> SICError:
        """
        SICエラー文字列を構造化データに変換
        
        Args:
            sic_error: SICエラーメッセージ
            
        Returns:
            SICError データクラス
        """
        # ガード節: None/空文字の場合
        if not sic_error:
            return SICError(
                error_type="UNKNOWN",
                affected_class=None,
                affected_items=[],
                human_readable="Unknown SIC error"
            )
        
        error_type = "UNKNOWN"
        affected_class = None
        affected_items = []
        
        # パターンマッチングでエラータイプを判定
        if "leaked" in sic_error.lower() and "top-level" in sic_error.lower():
            error_type = "TOP_LEVEL_POLLUTION"
            match = re.search(r"Attribute '(\w+)' leaked from class '(\w+)'", sic_error)
            if match:
                affected_items = [match.group(1)]
                affected_class = match.group(2)
                
        elif "count" in sic_error.lower() and "decreased" in sic_error.lower():
            error_type = "SYMBOL_DELETION"
            if "class" in sic_error.lower():
                error_type = "CLASS_DELETION"
            elif "function" in sic_error.lower():
                error_type = "FUNCTION_DELETION"
                
        elif "missing" in sic_error.lower() and "class" in sic_error.lower():
            error_type = "ATTRIBUTE_DRIFT"
            match = re.search(r"attribute '(\w+)' missing from class '(\w+)'", sic_error, re.IGNORECASE)
            if match:
                affected_items = [match.group(1)]
                affected_class = match.group(2)
                
        elif "disappeared" in sic_error.lower():
            error_type = "CLASS_DELETION"
            match = re.search(r"Class '(\w+)' disappeared", sic_error)
            if match:
                affected_class = match.group(1)
                
        elif "type" in sic_error.lower() and ("lost" in sic_error.lower() or "annotation" in sic_error.lower()):
            error_type = "TYPE_ANNOTATION_DRIFT"
            
        elif "syntax" in sic_error.lower():
            error_type = "SYNTAX_ERROR"
        
        return SICError(
            error_type=error_type,
            affected_class=affected_class,
            affected_items=affected_items,
            human_readable=sic_error
        )

    def _generate_sic_diagnosis(
        self,
        error: SICError,
        original_code: str,
        failed_code: str
    ) -> Dict[str, str]:
        """
        SICエラーから具体的な診断メッセージを生成
        
        Args:
            error: 構造化されたSICエラー
            original_code: 修正前のコード
            failed_code: SICで弾かれたコード
            
        Returns:
            {"error_type": str, "details": str, "examples": str}
        """
        error_type = error.error_type
        details = error.human_readable
        examples = ""
        
        if error_type == "TOP_LEVEL_POLLUTION":
            attr = error.affected_items[0] if error.affected_items else "ATTRIBUTE"
            cls = error.affected_class or "CLASS"
            
            details = (
                f"Attribute '{attr}' was INSIDE class '{cls}' in [DNA: ORIGINAL], "
                f"but YOUR output moved it to module level (top-level). "
                f"This BREAKS the class structure and will cause AttributeError at runtime."
            )
            
            examples = f"""**What went wrong:**

In [DNA: ORIGINAL] (CORRECT):
```
class {cls}:
    {attr} = ...  # ← Correctly INSIDE the class (4-space indent)
```

In [FAILURE: YOUR OUTPUT] (WRONG):
```
{attr} = ...  # ← WRONG: At module level (no indent)
class {cls}:
    ...  # ← The attribute is missing from here!
```

**How to fix:**
Keep ALL class attributes INSIDE their class definition with proper indentation (4 spaces).
"""

        elif error_type == "CLASS_DELETION":
            cls = error.affected_class or "SOME_CLASS"
            
            details = (
                f"Class '{cls}' exists in [DNA: ORIGINAL] but is MISSING from your output. "
                f"You accidentally deleted or renamed it."
            )
            
            examples = f"""**What went wrong:**

[DNA: ORIGINAL] has:
```
class {cls}:
    ...
```

[FAILURE: YOUR OUTPUT] is MISSING this class entirely.

**How to fix:**
Make sure ALL classes from [DNA: ORIGINAL] are present in your output.
"""

        elif error_type == "FUNCTION_DELETION":
            details = (
                f"Functions were deleted from your output. "
                f"The function count significantly decreased compared to [DNA: ORIGINAL]."
            )
            
            examples = """**What went wrong:**

[DNA: ORIGINAL] has several functions defined.
[FAILURE: YOUR OUTPUT] is missing some of these functions.

**How to fix:**
Do NOT remove any existing functions. Preserve all function definitions from [DNA: ORIGINAL].
"""

        elif error_type == "SYMBOL_DELETION":
            details = (
                f"Symbols (classes or functions) were deleted from your output. "
                f"The count significantly decreased compared to [DNA: ORIGINAL]."
            )
            
            examples = """**What went wrong:**

[DNA: ORIGINAL] has a certain number of classes/functions.
[FAILURE: YOUR OUTPUT] has fewer classes/functions.

**How to fix:**
Do NOT remove any existing classes or functions. Preserve all definitions from [DNA: ORIGINAL].
"""

        elif error_type == "ATTRIBUTE_DRIFT":
            attr = error.affected_items[0] if error.affected_items else "ATTRIBUTE"
            cls = error.affected_class or "CLASS"
            
            details = (
                f"Critical attribute '{attr}' exists in class '{cls}' in [DNA: ORIGINAL], "
                f"but is MISSING from your output."
            )
            
            examples = f"""**What went wrong:**

[DNA: ORIGINAL] has:
```
class {cls}:
    {attr} = ...  # ← This attribute exists
```

[FAILURE: YOUR OUTPUT]:
```
class {cls}:
    # {attr} is MISSING!
```

**How to fix:**
Do NOT remove existing attributes. Preserve all attributes from [DNA: ORIGINAL].
"""

        elif error_type == "TYPE_ANNOTATION_DRIFT":
            details = (
                f"Type annotations were lost in your output. "
                f"Variables that had type hints (e.g., `x: str = ...`) were changed to untyped (e.g., `x = ...`)."
            )
            
            examples = """**What went wrong:**

[DNA: ORIGINAL] (CORRECT):
```
DATABASE_URL: str = "sqlite:///..."  # ← Has type hint `: str`
```

[FAILURE: YOUR OUTPUT] (WRONG):
```
DATABASE_URL = "sqlite:///..."  # ← Type hint removed
```

**How to fix:**
Preserve ALL type annotations exactly as they appear in [DNA: ORIGINAL].
"""

        elif error_type == "SYNTAX_ERROR":
            details = (
                f"Your output has a Python syntax error. "
                f"The code cannot be parsed. Check for: missing colons, unmatched brackets, indentation errors."
            )
            
            examples = """**Common syntax errors:**
- Missing `:` after `if`, `for`, `def`, `class`
- Unmatched `(`, `)`, `[`, `]`, `{`, `}`
- Incorrect indentation (mixing tabs and spaces)
- Missing closing quotes in strings

**How to fix:**
Ensure your output is valid Python that can be parsed by `ast.parse()`.
"""

        else:
            details = error.human_readable
            examples = """**How to fix:**
Compare [DNA: ORIGINAL] and [FAILURE: YOUR OUTPUT] carefully.
Identify what structural changes you made, and undo them while keeping only the necessary fix.
"""

        return {
            "error_type": error_type.replace("_", " "),
            "details": details,
            "examples": examples
        }


    def _fix_html_script_reference_for_vanilla_js(
        self,
        all_files: Dict[str, str]
    ) -> Dict[str, str]:
        """
        フレームワーク禁止モード時、index.htmlの<script>参照をvanilla JSに修正
        
        安全策:
        1. _no_framework_mode が True の場合のみ実行
        2. script.js が存在し、かつスタブでないことを確認
        3. React/Vue ファイル（.jsx/.tsx/.vue）が存在することを確認
        4. 修正内容をログに記録
        """
        if not getattr(self, '_no_framework_mode', False):
            return all_files
        
        # script.js を検索
        vanilla_js_file = None
        vanilla_js_content = None
        for filename in all_files:
            if filename == 'script.js' or filename.endswith('/script.js'):
                vanilla_js_file = filename
                vanilla_js_content = all_files[filename]
                break
        
        if not vanilla_js_file or not vanilla_js_content:
            logger.debug("[HTML Fix] No script.js found, skipping")
            return all_files
        
        # スタブチェック: script.js が実コードを含むか確認
        lines = vanilla_js_content.strip().split('\n')
        non_empty_lines = [l for l in lines if l.strip()]
        
        if not non_empty_lines:
            logger.debug("[HTML Fix] script.js is empty, skipping")
            return all_files
        
        comment_patterns = ('//', '#', '*', '/*', '<!--', '"""', "'''")
        comment_lines = [l for l in non_empty_lines 
                        if l.strip().startswith(comment_patterns) or 
                        l.strip() in ('*/', '-->', '"""', "'''")]
        comment_ratio = len(comment_lines) / len(non_empty_lines)
        
        stub_indicators = ['not used', 'stub', 'placeholder', 'this file is', 'does nothing']
        has_stub_indicator = any(ind in vanilla_js_content.lower() for ind in stub_indicators)
        
        if comment_ratio > 0.8 or has_stub_indicator:
            logger.debug(f"[HTML Fix] script.js is stub (comment_ratio={int(comment_ratio*100)}%, stub_indicator={has_stub_indicator}), skipping")
            return all_files
        
        # React/Vueファイルが存在するか確認（修正が必要な状況かどうか）
        framework_files = [f for f in all_files if f.endswith(('.jsx', '.tsx', '.vue'))]
        if not framework_files:
            logger.debug("[HTML Fix] No framework files found, no fix needed")
            return all_files
        
        # HTMLファイルを検索して修正
        import re
        modified_files = []
        
        for filename in all_files:
            if not filename.endswith('.html'):
                continue
            
            content = all_files[filename]
            original_content = content
            
            # React/Vueエントリーポイントへの参照パターン
            patterns_to_replace = [
                # <script src="app.js" type="module">...</script>
                (r'<script\s+src=["\']app\.js["\']\s+type=["\']module["\'][^>]*>\s*</script>', 
                 f'<script src="{vanilla_js_file}"></script>'),
                # <script type="module" src="app.js">...</script>
                (r'<script\s+type=["\']module["\']\s+src=["\']app\.js["\'][^>]*>\s*</script>', 
                 f'<script src="{vanilla_js_file}"></script>'),
                # <script src="./src/App.jsx" ...>...</script>
                (r'<script[^>]*src=["\']\.?/?src/[^"\']+\.jsx["\'][^>]*>\s*</script>', 
                 f'<script src="{vanilla_js_file}"></script>'),
            ]
            
            for pattern, replacement in patterns_to_replace:
                if re.search(pattern, content, re.IGNORECASE):
                    content = re.sub(pattern, replacement, content, flags=re.IGNORECASE)
            
            if content != original_content:
                all_files[filename] = content
                modified_files.append(filename)
                logger.debug(f"[HTML Fix] Modified {filename}: script reference -> {vanilla_js_file}")
        
        if modified_files:
            logger.debug(f"[HTML Fix] Fixed {len(modified_files)} HTML file(s): {modified_files}")
        
        return all_files

    def retry_full_validation(
        self,
        generated_code: Dict[str, str],
        goal: str,
        prev_lint_result: Optional[Dict[str, Any]] = None,
        attempt: int = 1,
        max_attempts: int = 3
    ) -> Tuple[Dict[str, str], Optional[Dict[str, Any]], Dict[str, float]]:
        """
        全チェック再実行（[t] Try again用）
        
        Gチェック(G-1〜G-31) → Import Test → Lint → Quality → 総合レビュー
        の全工程を再実行する。StepHUDでCode Generationスタイルの進捗表示。
        
        Args:
            generated_code: 生成されたコード
            goal: 元のゴール
            prev_lint_result: 前回のLint結果（改善検出用・エラー時の返却用）
            attempt: 現在の試行回数（StepHUDタイトル用）
            max_attempts: 最大試行回数（StepHUDタイトル用）
        
        Returns:
            Tuple[updated_code, lint_result, quality_scores]
        """
        try:
            from rich.console import Console
            console = Console()
        except ImportError:
            console = err_console
        
        try:
            from cognix import hud_components as hud_comp
        except ImportError:
            class HudCompFallback:
                @staticmethod
                def lint_summary_line(ok, warn, fail):
                    from rich.text import Text
                    return Text.from_ansi(f"ⓘ Lint Summary   OK {ok} / WARN {warn} / FAIL {fail}")
            hud_comp = HudCompFallback()
        
        # ============================================
        # StepHUD初期化（Code Generationスタイル）
        # ============================================
        hud = None
        try:
            from cognix.progress_zen import StepHUD
            steps = [
                "Consistency checks",
                "Import validation",
                "Lint check & Auto-fix",
                "Quality assessment",
                "Comprehensive Review",
            ]
            _hud = StepHUD(section_title="Retry Validation", steps=steps)
            _hud.start()
            hud = _hud  # start()成功後にのみ代入
        except ImportError:
            logger.debug("[Retry Validation] StepHUD not available, using fallback display")
            # フォールバック: シンプルな開始メッセージを表示
            console.print(f"{Icon.GEAR.value} Retry Validation...")
        except Exception as e:
            logger.debug(f"[Retry Validation] StepHUD init error: {e}")
            # フォールバック: シンプルな開始メッセージを表示
            console.print(f"{Icon.GEAR.value} Retry Validation...")
        
        try:
            logger.debug("[Full Validation] Starting full validation retry...")
            logger.debug(f"\n{Icon.GEAR.value} Running full validation...")
            
            # ⭐ Zen HUD用サマリー初期化（retry用）
            self._zen_summary = {
                "lint": {"initial": 0, "final": 0, "fixed": False},
                "review": {"initial": 0, "final": 0, "fixed": False, "issues": []}
            }
            
            # ============================================
            # Step 1: Gチェック（G-1〜G-31 全て再実行）
            # ============================================
            if hud:
                hud.mark("Consistency checks")
            logger.debug(f"  {Icon.GEAR.value} Running consistency checks...")
            
            MAX_FIX_ATTEMPTS = 2
            
            # G-1: HTML-CSS クラス整合性
            missing_css_classes = self._validate_html_css_class_consistency(generated_code)
            if missing_css_classes:
                logger.debug(f"[G-1] Detected {len(missing_css_classes)} missing CSS classes")
                generated_code = self._auto_complete_missing_css_classes(missing_css_classes, generated_code)
            
            # G-2: HTML-JS ID整合性
            missing_html_ids = self._validate_html_js_id_consistency(generated_code)
            if missing_html_ids:
                logger.debug(f"[G-2] Detected {len(missing_html_ids)} missing HTML IDs")
                generated_code = self._auto_complete_missing_html_ids(missing_html_ids, generated_code)
            
            # G-3: Python環境-パッケージ互換性
            compatibility_issues = self._validate_python_package_compatibility(generated_code)
            if compatibility_issues:
                logger.debug(f"[G-3] Detected {len(compatibility_issues)} compatibility issues")
                generated_code = self._auto_fix_package_compatibility(compatibility_issues, generated_code)
            
            # G-4: Pythonシンボルレベルimport整合性
            symbol_issues = self._validate_python_symbol_imports(generated_code)
            if symbol_issues:
                logger.debug(f"[G-4] Detected {len(symbol_issues)} symbol import issues")
                generated_code = self._auto_fix_symbol_imports(symbol_issues, generated_code)
            
            # G-5: Import vs Requirements.txt 整合性チェック
            requirements_issues = self._validate_import_requirements_consistency(generated_code)
            if requirements_issues:
                logger.debug(f"[G-5] Detected {len(requirements_issues)} missing packages in requirements.txt")
                generated_code = self._auto_fix_requirements_txt(requirements_issues, generated_code)
            
            # G-6: ライブラリ要求パターンチェック
            library_issues = self._validate_library_requirements(generated_code)
            if library_issues:
                logger.debug(f"[G-6] Detected {len(library_issues)} library requirement issues")
                generated_code = self._auto_fix_library_requirements(library_issues, generated_code)
            
            # G-7: フロントエンド依存整合性チェック
            frontend_issues = self._validate_frontend_dependencies(generated_code)
            if frontend_issues:
                logger.debug(f"[G-7] Detected {len(frontend_issues)} frontend dependency issues")
                generated_code = self._auto_fix_frontend_dependencies(frontend_issues, generated_code)
            
            # G-8: Model↔Schema整合性チェック
            schema_issues = self._validate_model_schema_consistency(generated_code)
            if schema_issues:
                logger.debug(f"[G-8] Detected {len(schema_issues)} model-schema consistency issues")
                generated_code = self._auto_fix_model_schema_consistency(schema_issues, generated_code)
            
            # G-9: ドキュメント環境依存チェック
            doc_issues = self._validate_document_environment_commands(generated_code)
            if doc_issues:
                logger.debug(f"[G-9] Detected {len(doc_issues)} environment-dependent commands")
                generated_code = self._auto_fix_document_environment_commands(doc_issues, generated_code)
            
            # G-10: クラス定義安全性チェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                class_safety_issues = self._validate_class_definition_safety(generated_code)
                if not class_safety_issues:
                    break
                logger.debug(f"[G-10] Detected {len(class_safety_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_class_definition_safety(class_safety_issues, generated_code)
            
            # G-11: フレームワーク予約語チェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                framework_reserved_issues = self._validate_framework_reserved_words(generated_code)
                if not framework_reserved_issues:
                    break
                logger.debug(f"[G-11] Detected {len(framework_reserved_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_framework_reserved_words(framework_reserved_issues, generated_code)
            
            # G-12: Python組み込み名シャドウイングチェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                builtin_issues = self._validate_python_builtin_shadowing(generated_code)
                if not builtin_issues:
                    break
                logger.debug(f"[G-12] Detected {len(builtin_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_python_builtin_shadowing(builtin_issues, generated_code)
            
            # G-13: Flask Blueprintルート重複チェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                route_duplicate_issues = self._validate_flask_route_duplicates(generated_code)
                if not route_duplicate_issues:
                    break
                logger.debug(f"[G-13] Detected {len(route_duplicate_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_flask_route_duplicates(route_duplicate_issues, generated_code)
            
            # G-14: SQLAlchemyテーブル名重複チェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                table_duplicate_issues = self._validate_sqlalchemy_table_duplicates(generated_code)
                if not table_duplicate_issues:
                    break
                logger.debug(f"[G-14] Detected {len(table_duplicate_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_sqlalchemy_table_duplicates(table_duplicate_issues, generated_code)
            
            # G-15: JavaScript予約語チェック（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                js_reserved_issues = self._validate_javascript_reserved_words(generated_code)
                if not js_reserved_issues:
                    break
                logger.debug(f"[G-15] Detected {len(js_reserved_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_javascript_reserved_words(js_reserved_issues, generated_code)
            
            # G-16: SQLAlchemy relationship参照先検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                relationship_issues = self._validate_relationship_references(generated_code)
                if not relationship_issues:
                    break
                logger.debug(f"[G-16] Detected {len(relationship_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_relationship_references(relationship_issues, generated_code)
            
            # G-17: ForeignKey参照先検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                foreignkey_issues = self._validate_foreignkey_references(generated_code)
                if not foreignkey_issues:
                    break
                logger.debug(f"[G-17] Detected {len(foreignkey_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_foreignkey_references(foreignkey_issues, generated_code)
            
            # G-18: SQLAlchemy Enum定義検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                enum_usage_issues = self._validate_sqlalchemy_enum_usage(generated_code)
                if not enum_usage_issues:
                    break
                logger.debug(f"[G-18] Detected {len(enum_usage_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_sqlalchemy_enum_usage(enum_usage_issues, generated_code)
            
            # G-19: Mapped型アノテーション整合性検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                mapped_type_issues = self._validate_mapped_type_consistency(generated_code)
                if not mapped_type_issues:
                    break
                logger.debug(f"[G-19] Detected {len(mapped_type_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_mapped_type_consistency(mapped_type_issues, generated_code)
            
            # G-20: Flask静的パス検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                flask_static_issues = self._validate_flask_static_paths(generated_code)
                if not flask_static_issues:
                    break
                logger.debug(f"[G-20] Detected {len(flask_static_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_flask_static_paths(flask_static_issues, generated_code)
            
            # G-21: HTML参照整合性検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                html_asset_issues = self._validate_html_asset_references(generated_code)
                if not html_asset_issues:
                    break
                logger.debug(f"[G-21] Detected {len(html_asset_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_html_asset_references(html_asset_issues, generated_code)
            
            # G-22: JavaScriptアニメーションループパターン検証（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                animation_issues = self._validate_js_animation_loop(generated_code)
                if not animation_issues:
                    break
                logger.debug(f"[G-22] Detected {len(animation_issues)} issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_js_animation_loop(animation_issues, generated_code)
            
            # G-23: API契約整合性チェック（警告のみ、自動修正なし）
            api_contract_issues = self._validate_api_contract_consistency(generated_code)
            if api_contract_issues:
                logger.debug(f"[G-23] Detected {len(api_contract_issues)} API contract issues (warning only)")
            
            # G-24: back_populates相互参照チェック
            for attempt in range(MAX_FIX_ATTEMPTS):
                bp_issues = self._validate_back_populates_consistency(generated_code)
                if not bp_issues:
                    break
                logger.debug(f"[G-24] Detected {len(bp_issues)} back_populates issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_back_populates_consistency(bp_issues, generated_code)
            
            # G-25: ForeignKey型注釈欠如チェック
            for attempt in range(MAX_FIX_ATTEMPTS):
                fk_type_issues = self._validate_foreignkey_type_annotation(generated_code)
                if not fk_type_issues:
                    break
                logger.debug(f"[G-25] Detected {len(fk_type_issues)} ForeignKey type issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_foreignkey_type_annotation(fk_type_issues, generated_code)
            
            # G-26: Mapped型とSQLAlchemy型の不一致チェック
            for attempt in range(MAX_FIX_ATTEMPTS):
                mapped_mismatch_issues = self._validate_mapped_sqlalchemy_type_mismatch(generated_code)
                if not mapped_mismatch_issues:
                    break
                logger.debug(f"[G-26] Detected {len(mapped_mismatch_issues)} Mapped type mismatch issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_mapped_sqlalchemy_type_mismatch(mapped_mismatch_issues, generated_code)
            
            # G-27: relationship overlaps検出（警告のみ）
            overlap_issues = self._validate_relationship_overlaps(generated_code)
            if overlap_issues:
                logger.debug(f"[G-27] Detected {len(overlap_issues)} relationship overlap issues (warning only)")
            
            # G-28: 循環インポート検出（警告のみ）
            circular_import_issues = self._validate_circular_imports(generated_code)
            if circular_import_issues:
                logger.debug(f"[G-28] Detected {len(circular_import_issues)} circular import issues (warning only)")
            
            # G-29: Marshmallow Nested循環参照検出（警告のみ）
            nested_circular_issues = self._validate_marshmallow_nested_circular(generated_code)
            if nested_circular_issues:
                logger.debug(f"[G-29] Detected {len(nested_circular_issues)} Marshmallow nested circular issues (warning only)")
            
            # G-30: JavaScript this参照喪失チェック
            for attempt in range(MAX_FIX_ATTEMPTS):
                this_issues = self._validate_js_this_context_loss(generated_code)
                if not this_issues:
                    break
                logger.debug(f"[G-30] Detected {len(this_issues)} this context issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_js_this_context_loss(this_issues, generated_code)
            
            # G-31: Marshmallow API誤用検出（再検証ループ付き）
            for attempt in range(MAX_FIX_ATTEMPTS):
                marshmallow_api_issues = self._validate_marshmallow_api_usage(generated_code)
                if not marshmallow_api_issues:
                    break
                logger.debug(f"[G-31] Detected {len(marshmallow_api_issues)} Marshmallow API issues (attempt {attempt + 1})")
                generated_code = self._auto_fix_marshmallow_api_usage(marshmallow_api_issues, generated_code)
            
            logger.debug(f"  {Icon.SUCCESS.value} Consistency checks completed")
            
            if hud:
                hud.complete("Consistency checks")
            
            # ============================================
            # Step 2: Runtime Import Test
            # ============================================
            if hud:
                hud.mark("Import validation")
            logger.debug(f"  {Icon.GEAR.value} Running import test...")
            
            import_success, import_error, failing_file = self._validate_runtime_import(generated_code)
            
            if import_success:
                if import_error and import_error.startswith("EXTERNAL_PACKAGE:"):
                    package_name = import_error.replace("EXTERNAL_PACKAGE:", "")
                    logger.debug(f"[Runtime Import] Skipped - external package: {package_name}")
                else:
                    logger.debug(f"  {Icon.SUCCESS.value} Import test passed")
            else:
                logger.debug(f"  {Icon.WARNING.value} Import error: {import_error}")
                # 自動修正を試行
                for attempt in range(2):
                    fixed_code = self._fix_import_error_with_llm(generated_code, import_error, failing_file)
                    if fixed_code:
                        retry_success, retry_error, retry_file = self._validate_runtime_import(fixed_code)
                        if retry_success:
                            generated_code = fixed_code
                            logger.debug(f"  {Icon.SUCCESS.value} Import error fixed")
                            break
                        import_error = retry_error
                        failing_file = retry_file
                        generated_code = fixed_code
            
            if hud:
                hud.complete("Import validation")
            
            # ============================================
            # Step 3: Lint check & Auto-fix
            # ============================================
            if hud:
                hud.mark("Lint check & Auto-fix")
            logger.debug(f"  {Icon.GEAR.value} Running lint check...")
            
            lint_result = None
            try:
                from cognix.linter_integration import LinterIntegration
                
                linter = LinterIntegration()
                
                if linter.available_linters:
                    lint_result = linter.lint_generated_code(generated_code)
                    
                    if lint_result['has_errors']:
                        error_count = len(lint_result['errors'])
                        warning_count = len(lint_result['warnings'])
                        
                        # ⭐ Zen HUD: 初期Lintエラー数を記録
                        self._zen_summary["lint"]["initial"] = error_count
                        
                        logger.debug("")
                        logger.debug(hud_comp.lint_summary_line(0, warning_count, error_count))
                        
                        if lint_result['can_auto_fix']:
                            fixed_files = self._fix_linter_errors_with_llm(
                                generated_code,
                                lint_result['errors'],
                                lint_result.get('file_languages', {})
                            )
                            
                            if fixed_files:
                                generated_code = fixed_files
                                logger.debug(f"  {Icon.GEAR.value} Linter errors auto-fixed")
                                
                                # 再チェック
                                recheck_result = linter.lint_generated_code(generated_code)
                                lint_result = recheck_result
                                
                                error_count_after = len(recheck_result.get('errors', []))
                                warning_count_after = len(recheck_result.get('warnings', []))
                                
                                # ⭐ Zen HUD: Auto-fix後のLintエラー数を記録
                                self._zen_summary["lint"]["final"] = error_count_after
                                self._zen_summary["lint"]["fixed"] = (error_count_after < error_count)
                                
                                logger.debug("")
                                logger.debug("  After auto-fix:")
                                logger.debug(hud_comp.lint_summary_line(0, warning_count_after, error_count_after))
                    else:
                        logger.debug(f"  {Icon.SUCCESS.value} Lint check passed")
                else:
                    logger.debug("[Linter] No external linters available")
            except ImportError:
                logger.debug("[Linter] LinterIntegration not available")
            except Exception as e:
                logger.debug(f"[Linter] Error: {e}")
            
            if hud:
                hud.complete("Lint check & Auto-fix")
            
            # ============================================
            # Step 4: Quality check & fix
            # ============================================
            if hud:
                hud.mark("Quality assessment")
            logger.debug(f"  {Icon.GEAR.value} Checking code quality...")
            
            quality_scores = self._assess_code_quality(
                generated_code,
                "medium",
                goal,
                lint_result=lint_result
            )
            
            # Quality issues検出
            quality_issues = []
            for filename, score in quality_scores.items():
                if score < 0.70:
                    code = generated_code.get(filename, '')
                    ext = filename.split('.')[-1]
                    placeholder_count, detected_lines = self._detect_placeholders_in_comments(code, ext)
                    
                    if placeholder_count > 0:
                        quality_issues.append({
                            'filename': filename,
                            'score': score,
                            'issue_type': 'placeholder',
                            'count': placeholder_count,
                            'details': detected_lines[:3]
                        })
            
            # Quality修正
            if quality_issues:
                logger.debug(f"  {Icon.WARNING.value} Found {len(quality_issues)} quality issue(s)")
                
                fixed_files = self._fix_quality_issues_with_llm(generated_code, quality_issues)
                
                if fixed_files:
                    generated_code = fixed_files
                    logger.debug(f"  {Icon.SUCCESS.value} Quality issues fixed")
                    
                    # 再評価
                    quality_scores = self._assess_code_quality(
                        generated_code,
                        "medium",
                        goal,
                        lint_result=lint_result
                    )
            else:
                logger.debug(f"  {Icon.SUCCESS.value} Quality check passed")
            
            if hud:
                hud.complete("Quality assessment")
            
            # ============================================
            # Step 5: 総合レビュー
            # ============================================
            if hud:
                hud.mark("Comprehensive Review")
            logger.debug(f"  {Icon.GEAR.value} Running comprehensive review...")
            logger.debug(f"      This may take 2-5 minutes for complex projects.")
            
            generated_code = self._final_comprehensive_review(generated_code, goal)
            
            # 🆕 Comprehensive Review後のHTML参照修正（スタブ再生成後に必要）
            if getattr(self, '_no_framework_mode', False):
                logger.debug("[Post-Review] Checking HTML script references after comprehensive review...")
                generated_code = self._fix_html_script_reference_for_vanilla_js(generated_code)
            
            logger.debug(f"  {Icon.SUCCESS.value} Comprehensive review completed")
            
            if hud:
                hud.complete("Comprehensive Review")
            
            # ============================================
            # 最終Quality再評価
            # ============================================
            quality_scores = self._assess_code_quality(
                generated_code,
                "medium",
                goal,
                lint_result=lint_result
            )
            
            logger.debug(f"\n{Icon.SUCCESS.value} Full validation completed")
            
            # StepHUD終了（成功）- Quality Scores表示前に終了
            if hud:
                hud.finish(success=True)
            
            # Quality表示（Lint/Review情報を反映した総合評価）
            if quality_scores:
                console.print()
                
                # Lint情報を取得
                lint_errors = len(lint_result.get('errors', [])) if lint_result else 0
                lint_warnings = len(lint_result.get('warnings', [])) if lint_result else 0
                
                # Review情報を取得（_zen_summaryから）
                review_issues = 0
                if hasattr(self, '_zen_summary') and self._zen_summary:
                    review_issues = self._zen_summary.get("review", {}).get("final", 0)
                
                # 総合評価を計算・表示
                overall_score, quality_display = self._format_quality_display(
                    base_quality_scores=quality_scores,
                    lint_errors=lint_errors,
                    lint_warnings=lint_warnings,
                    review_issues=review_issues
                )
                
                # 総合評価を表示
                console.print(quality_display)
                console.print()  # 空行
                
                # 個別ファイルのスコアも表示（複数ファイルの場合のみ）
                if len(quality_scores) > 1:
                    from rich.text import Text
                    console.print("Quality Scores:")
                    for filename, score in quality_scores.items():
                        score_percent = int(score * 100)
                        # 色とグレード判定（cli_chat_workflow.pyと統一）
                        if score >= 0.9:
                            color = "\033[92m"  # GREEN
                            grade = "Excellent"
                        elif score >= 0.75:
                            color = "\033[92m"  # GREEN
                            grade = "Good"
                        elif score >= 0.6:
                            color = "\033[93m"  # YELLOW
                            grade = "Fair"
                        else:
                            color = "\033[93m"  # YELLOW
                            grade = "Needs Review"
                        reset = "\033[0m"
                        console.print(Text.from_ansi(f"  {color}{filename}{reset}: {score_percent}% {color}{grade}{reset}"))
                    console.print()
            
            return (generated_code, lint_result, quality_scores)
        
        except ImportError as ie:
            # インポートエラー時のエラーハンドリング
            import sys
            import traceback
            
            logger.error(f"[Full Validation] ImportError details:")
            logger.error(f"  Error message: {str(ie)}")
            logger.error(f"  Module name: {getattr(ie, 'name', 'unknown')}")
            logger.error(f"  Traceback:")
            for line in traceback.format_tb(sys.exc_info()[2]):
                logger.error(f"    {line.strip()}")
            
            logger.debug(f"{Icon.WARNING.value} Full validation failed: Import error")
            logger.debug(f"[DEBUG] Import failed: {ie}")
            
            # StepHUD終了（失敗）
            if hud:
                hud.finish(success=False)
            
            return (generated_code, prev_lint_result, {})
        
        except Exception as e:
            # 一般的なエラー時のエラーハンドリング
            import traceback
            
            logger.error(f"[Full Validation] Error during validation: {e}")
            logger.error(f"  Traceback:\n{traceback.format_exc()}")
            
            logger.debug(f"{Icon.ERROR.value} Full validation error: {e}")
            
            # StepHUD終了（失敗）
            if hud:
                hud.finish(success=False)
            
            return (generated_code, prev_lint_result, {})


# Utility functions for CLI integration
def create_engine(workspace_path: Optional[str] = None) -> SemiAutoEngine:
    """Create a new SemiAutoEngine instance"""
    return SemiAutoEngine(workspace_path)


def process_chat_message(engine: SemiAutoEngine, message: str, auto_apply: bool = False) -> Dict[str, Any]:
    """Process a chat message through the engine"""
    return engine.process_message(message, auto_apply)