Skip to content
Merged

Dev #256

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 14 additions & 29 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

### 第一選択: `make` ターゲット

Makefile は `nix develop --command bash -c "..."` でラップ済み。AI は基本これを使う。
Makefile は `nix develop --command bash -c "..."` でラップ済み。AI は基本これを使う。**最新の一覧と詳細は `make help`** で確認する(本表は AI が即時参照する代表的なターゲットのみ)。

| 用途 | コマンド |
|---|---|
Expand All @@ -23,6 +23,9 @@ Makefile は `nix develop --command bash -c "..."` でラップ済み。AI は
| Lint 自動修正 | `make lint-fix` |
| マイグレーション | `make migrate` / `make migrate-create MSG="..."` |
| インフラ validate | `make infra-validate` |
| コード重複検知 | `make dupe-check` (結果: `report/dupe/jscpd-report.json`) |

セットアップ詳細・各コマンドの目的は `docs/development.md` を参照。

### 第二選択: `nix develop --command` ラッパー

Expand Down Expand Up @@ -56,6 +59,7 @@ error: opening lock file "~/.cache/nix/fetcher-locks/...lock": Operation not per
- **過剰な抽象化を避ける**: PEP8 を守るな、PEP8 を理解した上で抽象化しろ。

言語別の詳細ルールは `.claude/rules/{backend,frontend,infra}/` を参照。
領域横断の共通ルール(DRY / 重複検知)は `.claude/rules/common/duplication.md` を参照。

## CI 確認ルール

Expand All @@ -64,12 +68,10 @@ error: opening lock file "~/.cache/nix/fetcher-locks/...lock": Operation not per
```bash
# 一括(最速・推奨)
make ci

# 個別
make lint-backend && make test-backend
make lint-frontend && make test-frontend && make build-frontend
```

詳細なローカル CI 手順・個別コマンドは `docs/development.md`「テスト・リント」セクションを参照。

### E2E テストのトリガー

以下のいずれかに該当する変更を行った場合、E2E を必ず実行する:
Expand Down Expand Up @@ -105,32 +107,15 @@ CI 定義: `.github/workflows/ci.yml`

> `rirekisho` は日本語ローマ字のため cSpell の警告が出るが無視してよい。

## 環境変数(必須)

```
TURSO_DATABASE_URL # Turso (libSQL) 接続 URL。ローカル: http://127.0.0.1:8080(turso dev)/ 本番: libsql://<db>.turso.io
TURSO_AUTH_TOKEN # Turso 認証トークン(Cloud Run では Secret Manager から注入)
JWT_PRIVATE_KEY # RS256署名用秘密鍵(PEM形式)
JWT_PUBLIC_KEY # RS256検証用公開鍵(PEM形式)
FIELD_ENCRYPTION_KEY # Fernet鍵
ADMIN_TOKEN # 管理者操作用トークン
CORS_ORIGINS # 例: https://devforge-dev.example.com
COOKIE_SECURE # 例: true
COOKIE_SAMESITE # lax / strict / none
INTERNAL_SECRET # Cloudflare Pages → Cloud Run 間の秘密ヘッダー値(local 環境では省略可)
```
## 環境変数

### オプション
**正本**:
- 環境変数名の定数定義: `backend/app/core/env_keys.py`
- 用途と注入経路の一覧: `docs/api.md`「環境変数」セクション
- 本番(Cloud Run)の env block: `infra/modules/cloud_run/main.tf`
- ローカル開発の env: `docker-compose.yml`

```
GITHUB_CLIENT_ID # GitHub OAuth Client ID
GITHUB_CLIENT_SECRET # GitHub OAuth Client Secret
CALLBACK_BASE_URL # GitHub OAuth redirect_uri のベース URL(例: https://app.devforge.app)。未設定時は x-forwarded-host から自動検出
LLM_PROVIDER # ollama / vertex
VERTEX_PROJECT_ID # Vertex AI 用
VERTEX_LOCATION # 例: asia-northeast1
VERTEX_MODEL # 例: gemini-2.5-flash-lite
```
backend 内で `os.getenv("XXX")` のように文字列リテラル直接参照は禁止。`from app.core import env_keys` した上で `os.getenv(env_keys.XXX)` を使う。新規環境変数を追加するときは env_keys.py のコメントに記載の手順(4 箇所同期)を必ず実行する。

## ADR(Architecture Decision Record)

Expand Down
1 change: 1 addition & 0 deletions .claude/rules/backend/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ paths:
- コード変更後は `make lint-backend` を実行し、違反がないことを確認すること(Nix devshell 経由で ruff が解決される)
- 特定ファイルだけ検証したい場合は `nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check <path>"` を使う。生シェルで `.venv/bin/python` を直接叩くのは禁止(WeasyPrint の動的ライブラリが解決できず import に失敗するため)
- 未使用の import を残さないこと(F401)
- 重複検知 / DRY ポリシーは `.claude/rules/common/duplication.md` を参照(抽出先は `backend/app/services/shared/` または同一サブパッケージの `_utils.py`)

## 例外処理の必須ルール

Expand Down
106 changes: 106 additions & 0 deletions .claude/rules/common/duplication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# コード重複 / DRY ポリシー(共通)

このルールは backend / frontend / infra すべての領域に適用される。
領域別のコーディング規約(`.claude/rules/{backend,frontend,infra}/`)と併せて参照すること。

## 原則

### Rule of Three

- **1 回目**: そのまま書く
- **2 回目**: 重複を認識する(まだ抽象化しない)
- **3 回目**: 抽出する。共通化先は下記「抽出先ヒエラルキー」に従う

2 回目で先回り抽象化すると、想定外の差分が出た時に逆に複雑化する。3 つ目の利用箇所が現れた時点で、共通点と差分が明確になっているはずなので、そこで初めて抽出する。

### 過剰な抽象化を避ける

CLAUDE.md にある通り「PEP8 を守るな、PEP8 を理解した上で抽象化しろ」。重複検知 (jscpd) のレポートに引っかかったからといって、機械的に DRY 化してはいけない。「同じ形をしているが意味が違う」コードは別物として残すべき。

判断基準:

- **形は同じだが変更理由が違う** → 抽出しない(偶発的重複)
- **形は違うが変更理由が同じ** → 抽出する(本質的重複)

## 禁止される重複(本質的重複)

以下が複数箇所に書かれていたら、原則として抽出対象とする。

- **ドメインロジック**: スコア計算 / 正規化 / バリデーション / 状態遷移
- **エラーマッピング**: バックエンドのエラーコード ↔ ユーザー向けメッセージの対応表
- **API パス文字列**: `/api/v1/...` のリテラルが複数モジュールに散在
- **環境変数名のリテラル**: `os.environ["TURSO_DATABASE_URL"]` のような文字列を `settings.py` 以外で参照
- **DTO / 型定義**: backend `app/schemas/` ↔ frontend `src/types.ts` の二重定義(同じフィールド構造を別言語で持つこと自体は許容、ただし片方の変更がもう片方の更新を忘れさせるなら検知できる仕組みが必要)

## 許容される類似(偶発的重複)

機械検出 (jscpd) で重複として検出されても、抽出してはいけないもの。

- **pytest fixture の最小スキャフォールド**: テスト個別の準備コード(`db_session` 等の共通 fixture と区別)
- **Pydantic schema の field 列**: 似た形のレスポンス schema を 1 つにまとめると変更理由が混ざる
- **`infra/environments/{dev,stg,prod}/terraform.tfvars` の同名キー**: 値が環境別なので冗長ではない
- **テストの arrange-act-assert ブロック**: 「同じ流れ」は読みやすさのために残す
- **JSDoc / docstring のテンプレート文言**: 「### Args」「### Returns」等のセクション見出し
- **import 文の塊**: 同じライブラリ群を多くのモジュールが import すること自体

## 抽出先ヒエラルキー

重複を抽出すると決めたら、以下の順で配置先を決める。

### Backend (FastAPI)

1. **同一サブパッケージ内の純粋関数** → 同じディレクトリの `_utils.py` か `_helpers.py`
2. **ドメイン横断のロジック** → `backend/app/services/shared/`
3. **永続化に関する重複** → `backend/app/repositories/base.py` の共通メソッド
4. **HTTP 入出力の変換** → `backend/app/routers/<scope>/_responses.py`
5. **モデル / DTO** → `backend/app/schemas/shared.py`

参考: 既存の `backend/app/services/shared/sort_utils.py` がドメイン横断 util の配置例。

### Frontend (React + TypeScript)

1. **状態管理を含む共通ロジック** → `frontend/src/hooks/` の新規フック(`useDocumentForm`, `useTaskPolling` パターン)
2. **純粋関数 / 文字列変換 / 日付処理** → `frontend/src/utils/`
3. **API クライアントの共通パターン** → `frontend/src/api/client.ts` のラッパー追加
4. **フォーム入出力変換** → `frontend/src/formMappers.ts` / `frontend/src/payloadBuilders.ts`
5. **共通 UI コンポーネント** → `frontend/src/components/ui/`(ErrorToast, Skeleton 等の配置例)
6. **型定義** → `frontend/src/types.ts` / `frontend/src/formTypes.ts`

### Infra (OpenTofu)

1. **2 環境以上で同じ resource block** → `infra/modules/` に切り出し、各 environment から呼ぶ
2. **環境別の値だけが違う構成** → モジュール側を `variable` 化、`environments/<env>/main.tf` で値を渡す
3. **モジュール内部の重複** → サブモジュール化は慎重に(HCL のサブモジュール深掘りは可読性を下げる)

参考: `infra/modules/cloud_run/` `artifact_registry/` `cloud_tasks/` `cloudflare/` `monitoring/` `service_account/` が既存モジュール例。

### 領域横断 (BE ↔ FE ↔ infra)

1. **エラーコード**: backend の `app/core/errors.py` を Single Source of Truth とし、frontend の `utils/appError.ts` は OpenAPI 経由で同期できないか検討する
2. **環境変数名**: backend の `app/core/settings.py` で定義したフィールド名を、infra 側 (`infra/modules/cloud_run/main.tf` の `env` ブロック) と CI (`.github/workflows/ci.yml`) で参照する。リテラル文字列のコピペは避ける
3. **手順書 / README / docs**: 重複しがちな手順は `docs/` に正本を置き、README からはリンクで参照する

## 検知の運用

### 機械検出 (jscpd)

`make dupe-check` で `report/dupe/jscpd-report.json` を生成する。Phase 1 は warn-only(CI 落とさない)。
PR 前に 1 度走らせて、新規重複が増えていないか確認する。

しきい値 (`.jscpd.json`):

- `minTokens: 50` / `minLines: 5` — これ未満の小片は無視
- `threshold: 0` — Phase 1 は fail させない(baseline 確定後に引き上げ)

### AI レビュー (refacter skill)

- 領域内: `BE_refacter` / `FE_refacter` / `INFRA_refacter`
- 領域横断: `XR_refacter`

各 skill は `report/dupe/jscpd-*.json` を読み込んでから、「形だけ似ているのか / 本質的に重複しているのか」を判定する。
本ルールの「禁止される重複」「許容される類似」を基準に分類する。

### Stop hook (`.claude/settings.local.json`)

Claude Code のセッション終了時に `make dupe-check` を background 実行する設定が入っている場合、
次のセッションで `report/dupe/jscpd-report.json` を最初に読むこと。古い情報を引きずらないように。
1 change: 1 addition & 0 deletions .claude/rules/frontend/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ paths:
- ESLint / Prettier の設定に従うこと
- リントは `make lint-frontend`、テストは `make test-frontend` を使う(Nix devshell 経由で解決される)
- 個別スクリプトを叩きたい場合は `nix develop --command bash -c "cd frontend && npm run <script>"` を使う。生シェルでの `cd frontend && npm ...` は AI エージェントでは禁止
- 重複検知 / DRY ポリシーは `.claude/rules/common/duplication.md` を参照(抽出先は `src/hooks/` `src/utils/` `src/components/ui/`)

## E2E テスト(Playwright)

Expand Down
5 changes: 5 additions & 0 deletions .claude/rules/infra/opentofu.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ CLI: OpenTofu (`tofu`) を使用する。Nix で管理されており `nix devel
デプロイ: GitHub Actions で `dev` ブランチ push 時に frontend → Cloudflare Pages、backend → Docker → Artifact Registry → Cloud Run。

DB は Turso (libSQL) を使用。Terraform 対象外で `turso CLI` 手動管理。詳細は `docs/data-model.md` の「Turso CLI セットアップ」参照。

## 重複・DRY

- 重複検知 / DRY ポリシーは `.claude/rules/common/duplication.md` を参照
- `environments/{dev,stg,prod}` で同じ resource block をコピペしている場合は `modules/` 化を検討する(環境差分は `variable` で吸収)
Loading
Loading