Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- 本ファイルは全体ルールの索引。AI エージェント(Claude Code 含む)が最初に読むべき内容を集約している。
- 領域固有ルール(backend / web / infra)は `.claude/rules/<scope>/*.md` に分割済み。対象パスを編集する際に自動でロードされる。重複は避け、詳細は各 rule ファイルへ寄せる。
- **DevForge Agent(`backend/app/services/agent/` / `backend/app/prompts/agent_*.md` / `backend/app/schemas/agent.py`)を変更する場合は、作業前に必ず `.claude/rules/backend/agent.md` を読むこと。** 制約の責務分離(スキーマ vs プロンプト)・エラー契約・DB 非更新原則など、意図せず壊しやすい不変条件が集約されている。
- **決定論的ロジック層(mutmut / stryker のミューテーション対象)を変更する場合は TDD(red→green→refactor)が必須。** 手順の正本は `.claude/rules/common/tdd.md`(ADR-0019)。テスト差分の随伴は `make lint-tdd`(`make ci` に含まれる)が機械検証する。

## AI エージェント実行方法

Expand Down Expand Up @@ -140,7 +141,7 @@ Secrets は未登録の間は通知が静かに skip される(CI は green

| 合言葉 | やること |
|---|---|
| **stage** | 実装 → `make ci` → `git add` まで。作業開始時にブランチを切り損ねて `main` にいた場合はここで feature ブランチを `origin/main` 起点で切る(本来は「作業開始時のブランチ運用」で切る)。会話に「サマリ+判断が必要な事案」を提示し、ユーザーのエディタ確認を待つ |
| **stage** | 実装 → `make ci` → `git add` まで。TDD 対象(`.claude/rules/common/tdd.md` の対象判定に該当)の変更は red→green→refactor を経てから stage に入る。作業開始時にブランチを切り損ねて `main` にいた場合はここで feature ブランチを `origin/main` 起点で切る(本来は「作業開始時のブランチ運用」で切る)。会話に「サマリ+判断が必要な事案」を提示し、ユーザーのエディタ確認を待つ |
| **commit** | コミットメッセージ案(**日本語**)を提示 → **ユーザー承認を待ってから** commit。承認は必須ゲート |
| **pr** | `git fetch origin main` → `git log --oneline origin/main..HEAD` / `git diff --stat origin/main...HEAD` で**最新の main との差分を確認**(ローカルの古い `origin/main` 参照で誤認しないため)→ `git push` → `gh pr create`(**日本語**タイトル/本文、base = `main`)→ PR URL を返す |
| **pr 後の追従** | PR 作成後、`gh pr checks` / `gh pr view --comments` で **CI と指摘を確認**。こけ・指摘があれば修正 → `make ci` → 同ブランチへ push を green かつ解消まで繰り返す。CI 修正は実装フェーズなので元のモデルで(Haiku のままにしない)。**ただし意思決定を要する指摘(設計・API/型契約・挙動変更)と diff 範囲を逸脱する指摘は、勝手に直さず指摘内容・対応案・影響範囲を提示して承認を待つ**。範囲内の機械的修正(lint / typo 等)は承認不要 |
Expand Down
4 changes: 4 additions & 0 deletions .claude/rules/backend/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ paths:

# Backend テスト方針

## TDD 対象の判定(最初に確認)

変更する実装ファイルが `backend/pyproject.toml` の `[tool.mutmut] only_mutate` に該当する場合(決定論的ビジネスロジック層)、**テストを先に書く TDD ワークフローが必須**。手順は `.claude/rules/common/tdd.md`(正本)に従うこと(ADR-0019。`make lint-tdd` が CI で検証する)。該当しない変更は以下のトリガーベース方針に従う。

## いつテストを書く・回すか(トリガー)

以下のいずれかに該当する変更を行った場合、テスト追加・更新と実行が必須:
Expand Down
71 changes: 71 additions & 0 deletions .claude/rules/common/tdd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
paths:
- backend/**
- web/**
---

# TDD ワークフロー(決定論的ロジック層)

決定論的ビジネスロジックの変更は **TDD(red → green → refactor)で行う**(ADR-0019)。
本ファイルが手順の正本。各領域の `test.md` は OK 基準・アンチパターンの正本であり、本ワークフローと併用する。

## 対象判定(最初に必ず行う)

変更しようとしている実装ファイルが以下の glob に該当するか確認する。**スコープの正本はミューテーションテスト設定**(ADR-0017 と共有。ここに複製しない):

- backend: `backend/pyproject.toml` の `[tool.mutmut] only_mutate`
- web: `web/stryker.conf.json` の `mutate`(`!` の除外パターン込み)

| 判定 | 従うルール |
|---|---|
| 該当する | 本ワークフロー(TDD)が**必須** |
| 該当しない(routers / schemas / UI コンポーネント / infra 等) | 各 `test.md` のトリガーベース方針 |

該当するのにテスト差分なしで PR を出すと `make lint-tdd`(`make ci` に含まれる)が fail する。振る舞いを変えない変更(リネーム・コメント修正・機械的リファクタ)は、コミットメッセージに `Tdd-Exempt: <理由>` トレーラーを付けて除外できる(理由はレビューで妥当性を見る)。

## red — 失敗するテストを先に書く

1. これから実装する**振る舞い 1 つ**について、期待挙動を表すテストを書く(実装コードにはまだ触れない)
2. 対象を絞って実行し、**期待どおりの理由で失敗すること**を確認する:

```bash
# backend
nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_<module>.py -q"
# web
nix develop --command bash -c "cd web && npx vitest run src/<path>/<module>.test.ts"
```

3. **失敗出力(要点)を会話・PR に提示**してから green に進む

禁止事項:

- **実装を先に書いてから「fail するはずだったテスト」を逆算で書く**(TDD の体裁だけ整える行為。実装なぞりテストの温床)
- **red の省略**: 「自明に失敗するはず」でも必ず実行する。import エラーや collection error での失敗は「期待どおりの理由の失敗」ではない(テスト自体が壊れている)
- **red フェーズで複数の振る舞いのテストを一括作成する**: 1 サイクル 1 振る舞い。次の振る舞いは次のサイクルで

## green — テストを通す最小実装

- red のテストを通す**最小限の実装**を書く。先回りの汎用化をしない(`duplication.md` の Rule of Three)
- **テスト側は触らない**。実装中にテストの仕様誤りに気づいた場合のみ、その旨と理由を報告した上で修正する(黙って assert を実装に合わせて弱めるのは禁止)
- 対象テストが pass したら、周辺の既存テストも回して回帰がないことを確認する

## refactor — green を維持したまま整理

- 重複の抽出・命名の改善・分割を行う。判断基準と抽出先は `.claude/rules/common/duplication.md` に従う
- リファクタ後に再度テストを回して green を維持していることを確認する
- サイクル完了後は通常のフロー(`make ci` → stage)に合流する

## OK 基準との関係

TDD で書いたテストも、各領域の既存基準をそのまま満たすこと:

- backend: `.claude/rules/backend/test.md` の OK 基準(主要分岐ごとに 1 ケース・失敗パスは `pytest.raises`・DB はモックしない等)
- web: `.claude/rules/web/test.md` の OK 基準(フックは loading/success/error の 3 パス等)

1 サイクル 1 振る舞いで進めるため、変更全体が終わった時点でこれらのケース数を満たしていればよい(1 red で全ケースを書く必要はない)。

## 検証コマンド

```bash
make lint-tdd # TDD 対象の実装変更にテスト差分が随伴しているか(make lint / make ci に含まれる)
```
4 changes: 4 additions & 0 deletions .claude/rules/web/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ paths:

# Frontend テスト方針

## TDD 対象の判定(最初に確認)

変更する実装ファイルが `web/stryker.conf.json` の `mutate`(`!` 除外込み)に該当する場合(utils / hooks / formMappers / payloadBuilders / store slice 等の決定論的ロジック層)、**テストを先に書く TDD ワークフローが必須**。手順は `.claude/rules/common/tdd.md`(正本)に従うこと(ADR-0019。`make lint-tdd` が CI で検証する)。該当しない変更は以下のトリガーベース方針に従う。

## いつテストを書く・回すか(トリガー)

### ユニット / コンポーネントテスト(vitest + node:test)
Expand Down
50 changes: 50 additions & 0 deletions .claude/skills/tdd/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: tdd
description: Use when implementing changes to the deterministic logic layer (mutmut/stryker mutation scope) with red→green→refactor phase control, or when the user explicitly asks for TDD. Presents test-case design first, writes a failing test and shows the failure output (red), writes the minimal implementation (green), then refactors while keeping tests green, and finally joins the normal stage flow. Trigger on requests such as "TDD で実装して", "/tdd", "テストファーストで", "red-green-refactor で進めて".
---

# TDD 段階制御(red → green → refactor)

決定論的ロジック層の実装を TDD で進めるための段階制御 skill。
**手順・禁止事項の正本は `.claude/rules/common/tdd.md`**(ADR-0019)。本 skill はそれをセッション内のフェーズ進行(停止・報告のタイミング)に落とす。

## 先に読む

- `.claude/rules/common/tdd.md`(ワークフロー正本)
- 対象領域の `test.md`(`.claude/rules/backend/test.md` / `.claude/rules/web/test.md` — OK 基準・アンチパターン)

## Phase 0: 対象判定とテスト設計の提示(停止点)

1. 変更対象の実装ファイルを特定し、TDD スコープ(backend: `[tool.mutmut] only_mutate` / web: stryker `mutate`)に該当するか判定して結果を報告する
- **スコープ外だった場合**: その旨を伝え、通常のトリガーベース方針で進めるか確認する(ユーザーが明示的に TDD を指定した場合は続行してよい)
2. 実装する**振る舞いの一覧**をテストケース案として提示する(1 行 1 振る舞い。対象領域の OK 基準のケース数を満たす構成にする)
3. **ユーザーの確認を待つ**。テスト設計への合意が取れてから red に進む

## Phase 1: red — 失敗するテストを書く

振る舞い一覧の先頭 1 つについて:

1. テストだけを書く(**実装コードには触れない**)
2. 対象を絞って実行する:
- backend: `nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_<module>.py -q"`
- web: `nix develop --command bash -c "cd web && npx vitest run src/<path>/<module>.test.ts"`
3. **失敗出力の要点を会話に提示する**。「期待どおりの理由での失敗」であることを確認する(import エラー・collection error はテスト自体の不備なので直してから再実行)

## Phase 2: green — 最小実装

1. red のテストを通す最小限の実装を書く(先回りの汎用化をしない)
2. 対象テストの pass を実行結果で提示する
3. テスト側は触らない。仕様誤りに気づいた場合のみ、理由を報告してから修正する

振る舞い一覧に残りがあれば Phase 1 に戻る(1 サイクル 1 振る舞い)。

## Phase 3: refactor — 整理して合流

1. green を維持したまま重複抽出・命名整理を行う(`.claude/rules/common/duplication.md` に従う。不要ならスキップしてよい)
2. 領域のテスト全体を回して回帰がないことを確認する
3. `make ci`(`lint-tdd` を含む)を通し、通常のコミット / PR フロー(stage 待ち)に合流する。stage 時のサマリに **red の失敗出力を確認済みであること**を含める

## 注意

- 全フェーズを一気に進めない。Phase 0 の確認と、各 red の失敗出力提示は省略しない
- 振る舞いを変えない変更しか残らなかった場合(純リファクタ等)は、TDD サイクルではなく `Tdd-Exempt: <理由>` トレーラーの適用を提案する
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ jobs:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
# lint-tdd が base ブランチとの merge-base diff を必要とするため全履歴を取得する。
fetch-depth: 0
# 読み取り専用ジョブのため checkout の認証情報をディスクに残さない(サプライチェーン保護)。
persist-credentials: false

Expand All @@ -162,6 +164,11 @@ jobs:
- name: Lint SSoT (ADR index)
run: bash scripts/lint-adr-index.sh

# TDD 対象(決定論的ロジック層 = mutmut/stryker スコープ)の実装変更に
# テスト差分が随伴しているかを検知(ADR-0019)。bash/git/awk/sed のみ。
- name: Lint SSoT (TDD test accompaniment)
run: bash scripts/lint-tdd.sh

- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
- PR タイトルは `<type>: <内容>` の形式(例: `feat: GitHub 連携スコア計算の追加`)
- セルフレビュー後にマージする

## テスト方針(TDD)

決定論的ロジック層(ミューテーションテスト対象と同一スコープ)の変更は **TDD(red → green → refactor)** で行う。手順の正本は [`.claude/rules/common/tdd.md`](.claude/rules/common/tdd.md)、判断の経緯は [ADR-0019](docs/adr/0019-tdd-for-logic-layer.md)、コマンドは [`docs/development.md`](docs/development.md) の「TDD」節を参照(ここには複製しない)。

## ADR(Architecture Decision Record)

### ADR とは
Expand Down
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
setup install-hooks install-backend install-web generate-keys \
dev dev-build dev-down dev-amd64 dev-amd64-build dev-web preview-web dev-proxy dev-proxy-only stripe-webhook \
test test-backend test-web mutation-backend mutation-web \
lint lint-backend typecheck-backend lint-web lint-web-messages lint-env-keys lint-adr-index lint-fix \
lint lint-backend typecheck-backend lint-web lint-web-messages lint-env-keys lint-adr-index lint-tdd lint-fix \
format format-check \
ci \
dupe-check dupe-check-html dupe-clean \
Expand Down Expand Up @@ -48,6 +48,7 @@ help:
@echo " lint-web-messages Frontend: setError等にリテラル日本語が渡っていないか検知"
@echo " lint-env-keys env名/エラーコードの SSoT drift を検知 (env_keys.py↔compose/cloud_run 双方向, リテラル参照禁止, errors.py↔errorCodes.ts)"
@echo " lint-adr-index ADR 索引の drift を検知 (docs/adr/README.md↔ADR ファイルの存在/ステータス/見出し番号)"
@echo " lint-tdd TDD 対象 (mutmut/stryker スコープ) の実装変更にテスト差分が随伴しているか検知 (ADR-0019)"
@echo " lint-fix リント自動修正 (ruff + eslint)"
@echo " format Prettier で整形"
@echo " format-check Prettier チェック"
Expand Down Expand Up @@ -159,7 +160,7 @@ mutation-backend:
mutation-web:
nix develop --command bash -c "cd web && npm run test:mutation"

lint: lint-backend typecheck-backend lint-web lint-web-messages lint-env-keys lint-adr-index
lint: lint-backend typecheck-backend lint-web lint-web-messages lint-env-keys lint-adr-index lint-tdd

lint-backend:
nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check app tests alembic_migrations"
Expand Down Expand Up @@ -191,6 +192,12 @@ lint-env-keys:
lint-adr-index:
bash scripts/lint-adr-index.sh

# TDD 対象(mutmut/stryker スコープの決定論的ロジック層)の実装変更にテスト差分が
# 随伴しているかを検知(ADR-0019)。対象 glob は mutation 設定から動的に読み出す。
# bash/git/awk/sed のみに依存するため nix wrap 不要。
lint-tdd:
bash scripts/lint-tdd.sh

lint-fix:
nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check --fix app tests alembic_migrations"
cd web && npm run lint:fix
Expand Down
Loading
Loading