# Documentation Standardization Guide

**対象プロジェクト**: noveler
**作成日**: 2025-10-25
**バージョン**: 1.0
**責任者**: Development Team

---

## 目次

1. [概要](#概要)
2. [CLAUDE.md形式の標準化](#claudemd形式の標準化)
3. [モジュールヘッダーコメント](#モジュールヘッダーコメント)
4. [関数/クラスdocstring](#関数クラスdocstring)
5. [型注釈のベストプラクティス](#型注釈のベストプラクティス)
6. [実装例集](#実装例集)
7. [チェックリスト](#チェックリスト)

---

## 概要

### 目的

novelerプロジェクトでは、コード品質と保守性を向上させるため、ドキュメント（docstring、コメント、型注釈）の標準化を行います。

本ガイドは以下を定義します：
- ✅ 全モジュール・クラス・関数の統一的なdocstring形式
- ✅ 型注釈の一貫性
- ✅ コメント記述の原則
- ✅ ドキュメンテーション検証プロセス

### 対象読者

- **開発者**: 新規コード作成時のガイドライン
- **レビュアー**: コードレビュー時の判定基準
- **アーキテクト**: ドキュメンテーション方針の決定

### 適用範囲

- ✅ Python ファイル（.py）
- ✅ 新規作成ファイル
- ✅ 大幅に修正したファイル
- ⏳ 既存ファイル（段階的に移行）

---

## CLAUDE.md形式の標準化

### 2.1 モジュールレベルの標準形式

すべてのPythonモジュール（.py）は、ファイルの冒頭に以下の形式でヘッダーコメントを記載します：

```python
# File: <相対パス/ファイル名.py>
# Purpose: <モジュールの責務を一文で説明>
# Context: <主要な依存関係、ドメイン制約、呼び出し元を簡潔に記載>
```

**例1: インフラストラクチャ層のツール**

```python
# File: src/noveler/infrastructure/json/mcp/servers/tool_registry.py
# Purpose: Register utility MCP tools (conversion/validation/file-info) on a given server instance
# Context: Called from JSONConversionServer._register_tools. Expects a server compatible with FastMCP
#          (or its stub) and a context providing converter, logger, output_dir, and formatting helpers.
```

**例2: ドメイン層のプロトコル**

```python
# File: src/noveler/domain/protocols/unit_of_work_protocol.py
# Purpose: Define the UnitOfWork pattern protocol for transactional consistency
# Context: Used by application layer for database operations. Domain layer remains independent
#          of concrete implementations via Protocol-based contracts.
```

**例3: 感情分析ツール**

```python
# File: src/mcp_servers/noveler/tools/emotion/physiology_checker_tool.py
# Purpose: Verify physiological responses in emotion descriptions against medical accuracy standards
# Context: Part of emotion analysis pipeline. Validates consistency between emotional expressions
#          and biological responses (heart rate, breathing, muscle tension, etc.).
```

### 2.2 ベストプラクティス

#### 必須要素

| 要素 | 目的 | 例 |
|------|------|-----|
| **File** | ファイルの位置を明確化 | `src/noveler/infrastructure/...` |
| **Purpose** | モジュール責務を一文で表現 | `Register utility tools on MCP server` |
| **Context** | 呼び出し元、依存関係、制約 | `Called from X. Expects Y. Must not import Z.` |

#### 禁止事項

- ❌ 複数行にわたる Purpose 説明
- ❌ 実装詳細（特定メソッド名など）をコメントに記載
- ❌ 時間的な情報（「TODO: いずれ修正」など）
- ❌ 開発者個人の注釈（「I changed this because...」）

---

## モジュールヘッダーコメント

### 3.1 位置と形式

ファイルの**最初の行**（shebang直後、import前）に配置：

```python
#!/usr/bin/env python3
"""Module docstring (optional, for sphinx compatibility)"""

# File: ...
# Purpose: ...
# Context: ...

from __future__ import annotations
from pathlib import Path
from typing import Any, Protocol
```

### 3.2 複数行の Context の書き方

複数の依存関係や制約がある場合は、複数行に分割して記載：

```python
# File: src/mcp_servers/noveler/server/tool_registry.py
# Purpose: Provide a thin, reusable function to register conversion/validation/file-info tools
#          on any FastMCP-like server instance
# Context: Called from JSONConversionServer._register_tools and other server implementations.
#          Expects a server compatible with FastMCP (or its stub) and a context providing
#          converter, logger, output_dir, and formatting helpers. Must not depend on
#          concrete server classes to maintain reusability across multiple server types.
```

### 3.3 Context 記載の原則

- ✅ **呼び出し元**: 「Called from X」「Used by Y」
- ✅ **依存関係**: 「Expects Z to provide...」
- ✅ **制約**: 「Must not import...」「Requires...」
- ✅ **ドメイン情報**: 「DDD compliance」「Structural typing」
- ❌ **詳細な実装**: 関数名、行番号など

---

## 関数/クラスdocstring

### 4.1 標準フォーマット

全関数・クラスには、以下の構成でdocstringを記載（英語推奨）：

```python
def function_name(param1: str, param2: int) -> dict[str, Any]:
    """Short description in one sentence.

    Longer description if needed, explaining intent and context.
    This paragraph should clarify the purpose beyond the short summary.

    Args:
        param1: Description of param1.
        param2: Description of param2.

    Returns:
        A dictionary containing the result.

    Raises:
        ValueError: When param2 is negative.
        TypeError: When param1 is not a string.

    Side Effects:
        Writes to the console via logger.info().
    """
```

### 4.2 関数docstringの構成

#### 必須セクション

| セクション | 内容 | 例 |
|-----------|------|-----|
| **Summary** | 一文の説明 | "Convert CLI output to JSON format" |
| **Description** | 詳細な説明（必要時） | "Explains when/why this is used" |
| **Args** | パラメータ説明 | `param_name: Type description` |
| **Returns** | 戻り値の説明 | "dict with 'success' key and results" |
| **Raises** | 発生する例外 | `ValueError: When input is invalid` |
| **Side Effects** | 副作用（I/O等） | "Writes to file system via converter" |

#### 形式の例

```python
async def execute(self, input_data: EmotionToolInput) -> EmotionToolOutput:
    """Verify physiological responses in emotion text.

    Analyzes emotion descriptions against medical accuracy standards.
    Detects inconsistencies between emotional intensity and physiological markers
    (heart rate, breathing, muscle tension, etc.).

    Args:
        input_data: Input containing text to analyze, emotion type, and intensity level.

    Returns:
        EmotionToolOutput with accuracy_score (0-100), detected inconsistencies,
        and correction suggestions.

    Raises:
        ValueError: When emotion type is not recognized.

    Side Effects:
        Logs analysis results via self.logger.
    """
```

### 4.3 クラスdocstringの構成

```python
class PhysiologyCheckerTool(BaseEmotionTool):
    """Verify physiological responses in emotion text.

    Analyzes emotion descriptions against medical accuracy standards by:
    1. Extracting physiological markers (heart rate, breathing, etc.)
    2. Validating against emotion-specific normal ranges
    3. Detecting medical inconsistencies
    4. Providing correction suggestions

    Medical data is based on established physiological ranges:
    - Fear: heart_rate 100-180 bpm, breathing 20-40 per min
    - Anger: heart_rate 90-160 bpm, breathing 18-35 per min
    - Sadness: heart_rate 50-80 bpm, breathing 8-15 per min
    - Joy: heart_rate 80-120 bpm, breathing 15-25 per min

    Attributes:
        emotion_physiology_map: Mapping of emotions to normal physiological ranges.
        physiology_patterns: Regex patterns for extracting physiological markers.
        inconsistent_combinations: Known medically inconsistent emotion/response pairs.

    Side Effects:
        Logs physiological database initialization.
    """
```

---

## 型注釈のベストプラクティス

### 5.1 完全な型注釈

全関数に対して、完全な型注釈を記載：

```python
# ✅ Good: 完全な型注釈
def _extract_numeric_value(self, text: str) -> float | None:
    """Extract numeric value from text.

    Args:
        text: Text to extract from.

    Returns:
        Extracted numeric value, or None if not found.
    """
    numbers = re.findall(r"\d+", text)
    return float(numbers[0]) if numbers else None

# ❌ Bad: 型注釈不完全
def _extract_numeric_value(self, text):  # ← 入力型なし
    """Extract numeric value."""
    numbers = re.findall(r"\d+", text)
    return float(numbers[0]) if numbers else None
```

### 5.2 Union型とOptionalの使い分け

```python
# ✅ 推奨: Union型（Python 3.10+）
def process(self, data: dict | None) -> list[str] | dict[str, Any]:
    """Process data and return result."""

# ✅ 従来形式（互換性重視）
from typing import Optional, Union
def process(self, data: Optional[dict]) -> Union[list[str], dict[str, Any]]:
    """Process data and return result."""

# ❌ 避けるべき形式
from typing import Optional
def process(self, data: Optional[dict]) -> Optional[Union[list, dict]]:
    """Unclear nesting of Optional."""
```

### 5.3 TypedDict の活用

複雑な構造を持つdictは、TypedDictで定義：

```python
# ✅ Good: TypedDict で構造を明確化
from typing import TypedDict

class RunJsonContent(TypedDict, total=False):
    """LangSmith run JSON content structure."""
    id: str
    run_id: str
    trace_url: str
    status: str
    tags: list[str]

class DestinationConfig(TypedDict, total=False):
    """Configuration for output destination."""
    path: str

def process_run(content: RunJsonContent) -> None:
    """Process LangSmith run content."""
    trace_url = content.get("trace_url")  # IDE自動補完あり
```

### 5.4 Generic型の活用

汎用パターンはGeneric型で定義：

```python
# ✅ Good: Generic型で再利用可能
from typing import Protocol, TypeVar, Generic

T = TypeVar('T')

class Repository(Protocol, Generic[T]):
    """Generic repository protocol."""

    def find_by_id(self, id: int) -> T | None:
        """Find entity by ID."""
        ...

    def save(self, entity: T) -> None:
        """Save entity."""
        ...

# 具体的な使用
class Episode:
    id: int
    title: str

class EpisodeRepository(Repository[Episode]):
    """Repository for Episodes."""
    pass
```

---

## 実装例集

### 6.1 ツール実装の例

```python
# File: src/mcp_servers/noveler/tools/emotion/physiology_checker_tool.py
# Purpose: Verify physiological responses in emotion descriptions for medical accuracy
# Context: Part of emotion analysis pipeline. Validates consistency between emotional
#          expressions and biological responses. Used by emotion_pipeline_coordinator.

from dataclasses import dataclass
from typing import Any
from .base_emotion_tool import BaseEmotionTool, EmotionIntensity, EmotionToolInput, EmotionToolOutput


@dataclass
class PhysiologicalResponse:
    """生理学的反応データ

    Attributes:
        response_type: Type of response (cardiac, respiratory, muscular, neural, hormonal).
        description: Human-readable description of the response.
        normal_range: Tuple of (min, max) normal values for this response type.
        intensity_factor: Correlation coefficient between emotion intensity and response.
    """
    response_type: str
    description: str
    normal_range: tuple[float, float]
    intensity_factor: float


class PhysiologyCheckerTool(BaseEmotionTool):
    """Verify physiological responses in emotion text.

    Analyzes emotion descriptions against medical accuracy standards by:
    1. Extracting physiological markers (heart rate, breathing, etc.)
    2. Validating against emotion-specific normal ranges
    3. Detecting medical inconsistencies
    4. Providing correction suggestions

    Medical ranges are based on established physiological standards:
    - Fear: elevated heart rate (100-180 bpm), rapid breathing (20-40 per min)
    - Anger: elevated cardiovascular response, muscle tension
    - Sadness: decreased activity, lower heart rate and breathing
    - Joy: moderate elevation, comfortable intensity

    Attributes:
        emotion_physiology_map: Emotion → physiological ranges mapping.
        physiology_patterns: Regex patterns for extracting physiological markers.
        inconsistent_combinations: Known medically implausible emotion/response pairs.

    Side Effects:
        Logs physiological database initialization.
    """

    def __init__(self) -> None:
        """Initialize the physiology checker.

        Purpose:
            Set up the tool with medical data and pattern definitions.

        Side Effects:
            Initializes physiological database in memory.
        """
        super().__init__("physiology_checker")
        self._initialize_physiological_database()

    async def execute(self, input_data: EmotionToolInput) -> EmotionToolOutput:
        """Verify physiological responses in emotion text.

        Purpose:
            Analyze emotion descriptions and validate physiological consistency.

        Args:
            input_data: Input containing text, emotion type, and intensity.

        Returns:
            EmotionToolOutput with accuracy score, inconsistencies, and suggestions.

        Raises:
            AttributeError: When emotion metadata is malformed.

        Side Effects:
            Logs analysis progress via self.logger.
        """
        text = input_data.text
        emotion = input_data.metadata.get("emotion") if input_data.metadata else None
        intensity = input_data.intensity or EmotionIntensity.MODERATE

        # Analyze physiological markers
        extracted_responses = self._extract_physiological_responses(text)
        accuracy_assessment = self._assess_medical_accuracy(extracted_responses, emotion, intensity)
        inconsistencies = self._detect_inconsistencies(extracted_responses, emotion, intensity)
        corrections = self._generate_corrections(inconsistencies, emotion, intensity)
        intensity_alignment = self._evaluate_intensity_alignment(extracted_responses, emotion, intensity)

        # Calculate comprehensive score
        analysis = {
            "extracted_responses": extracted_responses,
            "accuracy_score": accuracy_assessment["score"],
            "medical_accuracy": accuracy_assessment["details"],
            "inconsistencies": inconsistencies,
            "intensity_alignment": intensity_alignment,
            "response_count": len(extracted_responses),
            "coverage_by_type": self._analyze_response_coverage(extracted_responses)
        }

        score = (
            accuracy_assessment["score"] * 0.4 +
            intensity_alignment["score"] * 0.3 +
            max(0, 100 - len(inconsistencies) * 20) * 0.3
        )

        return self.create_success_output(
            score=score,
            analysis=analysis,
            suggestions=corrections,
            metadata={
                "emotion_analyzed": emotion,
                "intensity_level": intensity.value if intensity else None,
                "responses_found": len(extracted_responses)
            }
        )
```

### 6.2 インフラストラクチャレイヤーのプロトコル

```python
# File: src/noveler/infrastructure/json/mcp/servers/tool_registry.py
# Purpose: Register utility MCP tools with type-safe Protocol-based loose coupling
# Context: Used by multiple server implementations (JSONConversionServer, etc.)
#          to register conversion/validation/file-info tools without direct coupling.

from __future__ import annotations
from pathlib import Path
from typing import Any, Protocol


class _FormatterCtx(Protocol):
    """Minimal context required for tool registration.

    Purpose:
        Define the interface that server implementations must provide
        for tool registration. Uses Protocol for structural typing.

    Attributes:
        converter: Object exposing `convert(payload: dict) -> dict | None`.
        logger: Logger exposing `.exception(msg)` and `.info(msg, *args)`.
        output_dir: Path root for artefact operations.

    Side Effects:
        None (this is a Protocol, not an implementation).
    """

    converter: Any
    logger: Any
    output_dir: Path

    def _format_json_result(self, result: dict[str, Any]) -> str:  # noqa: D401
        """Format a JSON conversion result for textual display.

        Purpose:
            Provide a consistent textual representation of conversion output.

        Args:
            result: Conversion result payload to format.

        Returns:
            Formatted lines for display.

        Side Effects:
            None.
        """


def register_utility_tools(server: Any, ctx: _FormatterCtx) -> None:
    """Register conversion/validation/file-info tools on the server.

    Purpose:
        Centralise low-level tool registration to keep server classes thin
        and enable code reuse across multiple server implementations.

    Args:
        server: FastMCP-like server exposing a `.tool(...)` decorator.
        ctx: Host object satisfying _FormatterCtx Protocol.

    Returns:
        None

    Side Effects:
        Binds decorated tool functions to the provided server instance.
    """

    @server.tool(
        name="convert_cli_to_json",
        description="CLI実行結果をJSON形式に変換し、95%トークン削減とファイル参照アーキテクチャを適用",
    )
    def convert_cli_to_json(cli_result: dict[str, Any]) -> str:
        """Convert the CLI payload into a compressed JSON response.

        Purpose:
            Transform CLI outputs to JSON and summarise results.

        Args:
            cli_result: Raw CLI result mapping to convert.

        Returns:
            Human-readable summary with formatted JSON result.

        Raises:
            TypeError: When cli_result is not a dictionary.

        Side Effects:
            Writes artefacts through ctx.converter when configured.
            Logs errors via ctx.logger.exception().
        """
        try:
            if not cli_result:
                return "エラー: cli_resultパラメータが必要です"

            json_result = ctx.converter.convert(cli_result)
            return f"変換成功:\n{ctx._format_json_result(json_result)}"
        except Exception as e:  # pragma: no cover
            ctx.logger.exception("CLI→JSON変換エラー")
            return f"変換エラー: {e!s}"
```

---

## チェックリスト

### 新規ファイル作成時

- [ ] **ファイルヘッダー**: File/Purpose/Context を記載
- [ ] **モジュールdocstring**: 目的と責務を説明（オプション）
- [ ] **全関数に docstring**: 英語で Summary/Description/Args/Returns/Raises/Side Effects
- [ ] **全関数に型注釈**: 入出力の型を明示
- [ ] **複雑な dict**: TypedDict で構造を定義
- [ ] **Generic 処理**: Protocol で再利用可能に設計
- [ ] **mypy strict**: エラーなし（`mypy --strict`で検証）

### 既存ファイル修正時

- [ ] **関連する関数を修正**: docstring 更新（パラメータ変更時）
- [ ] **新規関数追加**: 完全な docstring + 型注釈
- [ ] **型注釈追加**: 既存の型注釈を不完全にしない
- [ ] **Side Effects 記載**: I/O 等があれば明記

### コードレビュー時（レビュアー）

- [ ] ファイルヘッダーが CLAUDE.md 形式に従っているか
- [ ] docstring が英語で、Purpose/Returns/Raises/Side Effects を含むか
- [ ] 型注釈が完全（入出力両方）か
- [ ] Complex dict は TypedDict を使用しているか
- [ ] Protocol を適切に活用しているか（ABC に頼りすぎていないか）

---

## 関連資料

- **CLAUDE.md**: `CLAUDE.md` - プロジェクト実装ルール
- **AGENTS.md**: `AGENTS.md` - 開発ワークフロー原則
- **Protocol Guidelines**: `docs/design/protocol_guidelines.md` - Protocol設計ガイド
- **Code Style**: `docs/guides/code_style_guide.md` (あれば参照)
- **SPEC-901**: `docs/architecture/SPEC-901_implementation_status.md` - DDD 実装状態

---

**記録日**: 2025-10-25
**作成者**: Claude Code (Haiku 4.5)
**ステータス**: ✅ アクティブ
