---
paths: ["src/**", "datasets/**"]
---

# コーディング規約 (Coding Standards)

Python コード（`src/**`）・マイグレーション（`src/tiasdata/catalog/migrations/**`）・データセットの定義（`datasets/**`）を編集するときは本ルールに従うこと．

## 基本方針 (Basic Policy)

- ruff を唯一の lint / format ツールとして使用する．設定は `pyproject.toml` の `[tool.ruff]` に集約する
- 標準（PEP 8）から逸脱しない範囲で ruff の自動修正に従う
- 別ツール（black・isort・mypy 等）は導入しない（ruff で完結させる）
- 使う側（TIASJudgeModel 等）と同じ規約にそろえる（サブモジュールとして同じ作業の中で編集されるため）

## 適用 ruff ルール (Ruff Rules)

`pyproject.toml` の `[tool.ruff.lint]` で `select = ["E", "F", "W", "I", "B", "UP", "N"]` を選択する．行長は 100 文字とする．実行コマンドは [ENV_04 開発コマンド](../../docs/02_ENV/ENV_04_開発コマンド.md)を参照する．

| ルール | 内容 |
| --- | --- |
| E・W (pycodestyle) | PEP 8 形式 |
| F (pyflakes) | 未使用変数・未定義シンボル等 |
| I (isort) | import 文の並び順 |
| B (flake8-bugbear) | バグになりやすいパターン |
| UP (pyupgrade) | Python 3.12 の書き方への置き換え |
| N (pep8-naming) | 命名 |

## 臨床データを扱うコードの規則 (Clinical Data Rules)

本プロジェクトの最重要規則．[PLAN_01 要件定義書](../../docs/03_PLAN/PLAN_01_要件定義書.md)の制約と [SPEC_03「出力の規則」](../../docs/04_SPEC/SPEC_03_データセットと受け渡し.md)に対応する．

- **データの場所をコードに書かない**．目録・ストア・取り込み元・計算サーバの場所は設定ファイル（`config.py` が読む）から受け取る．テスト用の既定値もコードに置かない
- **CLI の出力・ログ・例外メッセージに出すのは，件数・研究用 ID・SHA-256・種類名・所見の値・設定で付けた名前だけ**．実際の患者 ID・対応表の中身・画像の中身は出さない．元のファイル名（`source_name`）は既定では出さない
- **対応表（`identity.sqlite`）を開くのは取り込みの処理だけ**にする．他のモジュールから対応表を参照しない
- **原本（取り込み元）に書き込まない**．取り込み元は読み取り専用で開き，ストア・目録にだけ書く
- **ストアのファイルを書き換えない・消さない**．ストアへの書き込みは「同じ名前が無ければコピーし，コピー後にハッシュを照合する」だけにする
- `print` ではなく `logging` を使う．ただし CLI の結果表示（件数の表等）は標準出力に書いてよい

## 目録（SQLite）の規則 (Catalog Rules)

- SQL は `catalog/` の中にだけ書く．他のモジュールは `catalog/` の関数を呼ぶ
- **値は必ずプレースホルダ（`?`・`:name`）で渡す**．f-string・`%`・`+` で SQL に値を埋め込まない
- 接続ごとに `PRAGMA foreign_keys = ON` を有効にする
- 複数の表にまたがる登録・版の作成は 1 つのトランザクションで行う（`with conn:`）．途中で失敗したら何も残さない
- **凍結した版（`dataset_versions`・`dataset_members`・`dataset_member_annotations`）と所見（`annotations`）を UPDATE・DELETE しない**．所見の訂正は新しい行の追加と `superseded_by` の設定だけで行う
- 日時は UTC の ISO 8601（例: `2026-10-02T09:00:00Z`）で保存する

## マイグレーション (Migrations)

- スキーマの変更は `src/tiasdata/catalog/migrations/<系列>/NNNN_<内容>.sql`（系列は `catalog`・`identity`．4 桁の連番）を追加して行う．ファイルの中に `BEGIN`・`COMMIT`・`PRAGMA user_version` を書かない（適用側が囲む）．**適用済みのマイグレーションファイルは編集しない**
- 1 つのファイルは 1 つのトランザクションで適用できる内容にする
- 種類（`kinds`）の追加もマイグレーションで行い，同時に `src/tiasdata/kinds/` に値の型を追加する
- 表・列を変えたら [SPEC_02 データモデル](../../docs/04_SPEC/SPEC_02_データモデル.md)を先に更新する

## 型ヒント (Type Hints)

- **公開関数・クラス**（`tiasdata` から使う側に見えるもの）: 引数と戻り値に型ヒント必須
- **内部ヘルパ・テストコード**: 任意．書ける所は書く
- 目録の行・manifest の行・設定は pydantic のモデルで表す（辞書のまま受け渡さない）
- 静的型チェック（mypy / pyright）は当面導入しない

## 公開 API の互換 (Public API Compatibility)

- `tiasdata` の公開 API（`load`・`open_bundle`・`Bundle` 等）と `manifest.csv` の列は使う側が依存する．**名前・意味を変える・消す場合は，PR に使う側への影響を書き，SPEC_03 を更新する**
- 列や引数の追加（既存の使い方を壊さないもの）は自由に行ってよい

## docstring

- **公開関数・クラス**: docstring 必須．Google スタイル（`Args:` / `Returns:` / `Raises:`）．日本語可
- 例外を送出する公開関数は `Raises:` に書く

```python
def load(name: str, version: int) -> Bundle:
    """データセットの版を指定してバンドルを開く．

    Args:
        name: データセット名（例: `tongue_color`）．
        version: 版（1 以上）．

    Returns:
        開いたバンドル．

    Raises:
        ValueError: バンドルが見つからない，または manifest の SHA-256 が一致しない．
    """
```

## 命名規則 (Naming)

| 対象 | スタイル | 例 |
| --- | --- | --- |
| モジュール・パッケージ | snake_case | `bundle.py`・`tiasdata` |
| 関数・変数 | snake_case | `freeze_version`・`manifest_sha256` |
| クラス | PascalCase | `Bundle`・`CatalogConfig` |
| 定数 | UPPER_SNAKE_CASE | `SCHEMA_VERSION` |
| プライベート | 接頭辞 `_` | `_copy_to_store` |
| 種類名（`kinds.kind`）・データセット名 | snake_case の英小文字 | `tongue_crop_masked`・`tongue_color` |

## コメント (Comments)

- **WHY を書く**: 何をしているかは識別子で表し，コメントは「なぜこの実装か」「制約」「出典」に限る
- **TODO** は `# TODO(rintoHasegawa): 内容` の形で書く

## CLI 引数 (CLI Arguments)

- `argparse` 標準を使う．ヘルプ文は日本語可
- 短縮形より長い形を優先する（`--version`・`--to`）
- **データのパスを引数に取らない**．取り込み元・受け渡し先は設定ファイルで付けた名前で指定する（SSH のコマンドにデータの場所が現れないようにするため）

## 参考 (References)

- [PEP 8 — Style Guide for Python Code](https://peps.python.org/pep-0008/)
- [Google Python Style Guide — Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
- [ruff documentation](https://docs.astral.sh/ruff/)
- [SQLite — Foreign Key Support](https://www.sqlite.org/foreignkeys.html)
