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
14 changes: 8 additions & 6 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ Makefile は `nix develop --command bash -c "..."` でラップ済み。AI は
make に無い操作(特定ファイルだけ ruff したい等)の場合のみ使う:

```bash
nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check app/services/tasks/handlers/blog_summarize.py"
nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_worker_extended.py -q"
nix develop --command bash -c "cd backend && ruff check app/services/tasks/handlers/blog_summarize.py"
nix develop --command bash -c "cd backend && python -m pytest tests/test_worker_extended.py -q"
nix develop --command bash -c "cd web && npm run test:e2e"
```

python / pytest / ruff / alembic は devshell の Nix build 環境(`devforge-backend-env`)から PATH で解決される(`.venv` は廃止済み / ADR-0021 Phase 1)。

### 禁止: 生シェルでの直接実行

`cd backend && .venv/bin/python -m pytest ...` を nix の外で叩くと、`LD_LIBRARY_PATH` / `DYLD_LIBRARY_PATH` が未設定で WeasyPrint のインポートが `OSError: cannot load library 'libgobject-2.0-0'` で落ちる。AI は nix wrap を必ず通す。
`cd backend && python -m pytest ...` を nix の外で叩くと、backend の Python 環境(uv2nix build)自体が PATH に無く、あったとしても `LD_LIBRARY_PATH` / `DYLD_LIBRARY_PATH` が未設定で WeasyPrint のインポートが `OSError: cannot load library 'libgobject-2.0-0'` で落ちる。AI は nix wrap を必ず通す。

### Sandbox と nix の競合(重要)

Expand Down Expand Up @@ -97,7 +99,7 @@ nix develop --command bash -c "cd web && npm run test:e2e"
| 正本(変更したら) | 再生成コマンド | コミットすべき生成物 | CI ジョブ |
|---|---|---|---|
| backend の OpenAPI スキーマ(`app/schemas/` の Pydantic、router のシグネチャ・query/path パラメータ・**endpoint/schema の docstring**) | `make codegen-types` | `web/src/api/generated.ts`(`backend/openapi.json` は gitignore で対象外) | `codegen-drift`(ADR-0007) |
| backend の依存定義(`backend/pyproject.toml` の `[project.dependencies]`) | `cd backend && uv lock`(nix devshell 経由) | `backend/uv.lock` | `test-backend` / `codegen-drift` の `uv sync --locked`(ADR-0021 Phase 0) |
| backend の依存定義(`backend/pyproject.toml` の `[project.dependencies]`) | `nix develop --command bash -c "cd backend && uv lock"` | `backend/uv.lock` | `test-backend` の `uv lock --check`(ADR-0021 Phase 0/2。依存導入自体は uv2nix の Nix build) |

- **判定基準**: 「OpenAPI スペックに出るものを変えたか」。エンドポイントの追加・削除、リクエスト/レスポンス型の変更、query/path パラメータの増減はもちろん、**docstring の文言変更だけでも description として spec に反映される**ため再生成が要る(今回の codegen-drift はこれで発生)。
- backend の `app/schemas/` / `app/routers/` を触ったら、`make ci` 前に `make codegen-types` を回して `git diff web/src/api/generated.ts` を確認する。差分が出たら必ず同じ PR でコミットする。
Expand All @@ -109,10 +111,10 @@ CI 定義: `.github/workflows/ci.yml`

テストの検出力(弱い assertion / 実装なぞり)を週次のミューテーションテストで可視化し、CI 結果は用途別 Slack チャンネルへ通知する。詳細(ローカル実行・レポート確認・Secrets 登録手順)は `docs/development.md`「ミューテーションテスト」「Slack 通知」節が正本。

- **ローカル実行**: `make mutation-backend`(mutmut)/ `make mutation-web`(Stryker)。**フル実行は長時間**のため、対象を絞る場合は `nix develop --command bash -c "cd backend && .venv/bin/python -m mutmut run 'app.services.shared.sort_utils*'"` / `nix develop --command bash -c "cd web && npx stryker run --mutate 'src/utils/text.ts'"`
- **ローカル実行**: `make mutation-backend`(mutmut)/ `make mutation-web`(Stryker)。**フル実行は長時間**のため、対象を絞る場合は `nix develop --command bash -c "cd backend && python -m mutmut run 'app.services.shared.sort_utils*'"` / `nix develop --command bash -c "cd web && npx stryker run --mutate 'src/utils/text.ts'"`
- **対象スコープの正本**: backend = `backend/pyproject.toml` の `[tool.mutmut]`、web = `web/stryker.conf.json`。決定論的ビジネスロジックに限定(schemas / models / routers / 自動生成コード等は対象外)
- **CI**: `.github/workflows/mutation.yml`(週次 月曜 3:00 JST + workflow_dispatch。**PR/push では動かない・fail しない warn-only**)
- **pytest の `--cov` は addopts に戻さない**: mutmut 干渉回避のため Makefile / test.yml の呼び出し側で付与している(ADR-0017)
- **pytest の `--cov` は addopts に戻さない**: mutmut 干渉回避のため Makefile(`make test-backend`。CI も同ターゲットを呼ぶ)で付与している(ADR-0017)

| Slack Secret | 用途 | 送信元 workflow |
|---|---|---|
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/backend/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ paths:
- **DROP COLUMN は原則 `op.drop_column` を直接使う**: SQLite/libSQL 3.35+ は `ALTER TABLE ... DROP COLUMN` をサポートする。インデックス・FK・制約のない素のカラムはこれで消せる(テーブル再作成不要)
- **FK 参照される親テーブルに `batch_alter_table` を使わない**: batch は「新テーブル作成 → 旧テーブル DROP → リネーム」で動くため、`users` のように子テーブルから FK 参照される親を batch で触ると、旧テーブル DROP 時に libSQL(`foreign_keys=ON`)で `FOREIGN KEY constraint failed` になる。標準 SQLite ドライバは `foreign_keys` がデフォルト OFF で通ってしまい差異を見落とすので注意。子テーブル(他から参照されない)の `drop_column` でのみ batch は安全
- **マイグレーション/libsql ネイティブ経路は `make test-backend`(pytest)では通らないが、CI の `smoke-backend` ジョブで検証する**: pytest は `conftest.py` の `Base.metadata.create_all` でスキーマを作るため alembic を通らず、libsql ネイティブドライバの実行パスも踏まない。これを補うため CI は本番イメージを build → 実 libSQL を起動 → alembic 適用 → `/health`(DB 接続を検証)が 200 を返すことを確認する(`.github/workflows/ci.yml` の `smoke-backend`)。ローカルで個別に migration の upgrade/downgrade を確認したい場合は **実 libSQL に対して**行うこと:
- offline SQL の事前確認: `nix develop --command bash -c "cd backend && TURSO_DATABASE_URL='file:///tmp/x.db' .venv/bin/python -m alembic upgrade <from>:<to> --sql"`
- offline SQL の事前確認: `nix develop --command bash -c "cd backend && TURSO_DATABASE_URL='file:///tmp/x.db' alembic upgrade <from>:<to> --sql"`
- 実適用: docker stack を起動(`make dev-build` / 本番 arch で再現したいときは `make dev-amd64-build`)し `docker compose logs api` で適用成功を確認する
- 失敗した `batch_alter_table` が残す `_alembic_tmp_<table>` テーブルは `turso db shell http://localhost:8080 "DROP TABLE IF EXISTS _alembic_tmp_<table>"` で掃除する

Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/backend/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ paths:
- ruff に準拠すること(設定: `backend/pyproject.toml`)
- PEP8を守るな、PEP8を理解した上で抽象化しろ
- コード変更後は `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 に失敗するため
- 特定ファイルだけ検証したい場合は `nix develop --command bash -c "cd backend && ruff check <path>"` を使う(ruff は devshell の Nix build 環境から PATH 解決される / ADR-0021 Phase 1)。生シェルで python / ruff を直接叩くのは禁止(backend の Python 環境が PATH に無く、WeasyPrint の動的ライブラリも解決できない
- **lint 失敗時は当該ファイルだけ確認する**: `make lint-backend` が他ファイルの I001 等で落ちる場合、自分の変更分は上記の個別 `ruff check <touched_file>` で検証してから進める(既存違反を巻き込まない)
- 未使用の import を残さないこと(F401)
- 重複検知 / DRY ポリシーは `.claude/rules/common/duplication.md` を参照(抽出先は `backend/app/services/shared/` または同一サブパッケージの `_utils.py`)
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/backend/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ make test-backend # 全テスト

特定ファイルだけ回す場合:
```bash
nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_worker_extended.py -q"
nix develop --command bash -c "cd backend && python -m pytest tests/test_worker_extended.py -q"
```

### pytest が通らない経路(コンテナ起動スモーク)
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/common/tdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ paths:

```bash
# backend
nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_<module>.py -q"
nix develop --command bash -c "cd backend && python -m pytest tests/test_<module>.py -q"
# web
nix develop --command bash -c "cd web && npx vitest run src/<path>/<module>.test.ts"
```
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/BE_apply/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ sandbox が `~/.cache/nix/fetcher-locks/*.lock` で落ちる場合は `dangerous
特定ファイルだけ確認したい場合のみ:

```bash
nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check <touched_file>"
nix develop --command bash -c "cd backend && .venv/bin/python -m pytest <touched_test> -q"
nix develop --command bash -c "cd backend && ruff check <touched_file>"
nix develop --command bash -c "cd backend && python -m pytest <touched_test> -q"
```

lint / test に失敗したら、原因を直してから次の検証に進む。失敗を残したまま PR レポートを書かない。`--no-verify` 等で hook を skip しない。
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/BE_refacter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,6 @@ backend/app/
- `make test-backend`
- `make dupe-check`(重複検知。`report/dupe/jscpd-report.json` を生成。sandbox は無効化して実行)

特定ファイルだけ検証したい場合は `nix develop --command bash -c "cd backend && .venv/bin/python -m ruff check <path>"` を使う。生シェルで `.venv/bin/python` を直接叩くのは禁止(WeasyPrint の動的ライブラリが解決できず import に失敗する)。
特定ファイルだけ検証したい場合は `nix develop --command bash -c "cd backend && ruff check <path>"` を使う(devshell の Nix build 環境から PATH 解決 / ADR-0021 Phase 1)。生シェルで python / ruff を直接叩くのは禁止(backend の Python 環境が PATH に無く、WeasyPrint の動的ライブラリも解決できない)。

コード変更を含む場合は、少なくとも影響範囲のテストを回し、必要なら全件を回してください。
2 changes: 1 addition & 1 deletion .claude/skills/SEC_review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ description: Use when running a security review / vulnerability check against th
## 最低限の検証コマンド

- スキャンは grep / git ベースで破壊なし。差分対象は `git diff --name-only` で取得。
- 依存監査は nix wrap 経由で実行(生シェルで `.venv/bin/` を直接叩かない。WeasyPrint の動的ライブラリ解決に失敗する)。
- 依存監査は nix wrap 経由で実行(生シェルで python を直接叩かない。devshell 外では backend の Python 環境も WeasyPrint の動的ライブラリも解決できない)。
- `make lint-*` / `make test-*` は本 skill では必須としない(修正検証は `SEC_apply` 側で回す)。

実装変更は `SEC_apply` skill が担う。本 skill はレビューと提案までで止める。
2 changes: 1 addition & 1 deletion .claude/skills/tdd/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ description: Use when implementing changes to the deterministic logic layer (mut

1. テストだけを書く(**実装コードには触れない**)
2. 対象を絞って実行する:
- backend: `nix develop --command bash -c "cd backend && .venv/bin/python -m pytest tests/test_<module>.py -q"`
- backend: `nix develop --command bash -c "cd backend && 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 はテスト自体の不備なので直してから再実行)

Expand Down
46 changes: 20 additions & 26 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,46 @@ jobs:
# 読み取り専用ジョブのため checkout の認証情報をディスクに残さない(サプライチェーン保護)。
persist-credentials: false

- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
# dev と CI のビルド経路を Nix devshell(uv2nix build)へ一致させる(ADR-0021 Phase 2)。
# WeasyPrint のネイティブライブラリ・Python 3.13・依存パッケージはすべて devshell が提供する。
- name: Install Nix
uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0
with:
version: "latest"
enable-cache: true
cache-dependency-glob: "backend/uv.lock"
# flake inputs(nixpkgs / uv2nix 等)の GitHub 取得でレート制限に当たらないようにする
github_access_token: ${{ github.token }}

- name: Setup Python
run: uv python install 3.13

- name: WeasyPrint 用システムライブラリのインストール
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libpango-1.0-0 libpangoft2-1.0-0 libpangocairo-1.0-0 \
libglib2.0-0 libgobject-2.0-0 libffi-dev libcairo2
- name: Cache Nix store
uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
with:
primary-key: nix-devshell-${{ runner.os }}-${{ hashFiles('flake.nix', 'flake.lock', 'backend/pyproject.toml', 'backend/uv.lock') }}
restore-prefixes-first-match: nix-devshell-${{ runner.os }}-

# --locked: uv.lock が pyproject.toml と drift していたら fail する(ADR-0021 Phase 0)
- name: Install backend dependencies
working-directory: backend
run: uv sync --locked
- name: Build devshell (uv2nix)
run: nix develop --command true

# 生存ミュータントがあっても集計と通知は行うため continue-on-error にする
# 生存ミュータントがあっても集計と通知は行うため continue-on-error にする。
# nix develop は flake.nix のあるリポジトリルートで実行する必要があるため
# working-directory は使わず、コマンド内で cd する。
- name: Run mutmut
id: run
working-directory: backend
continue-on-error: true
run: uv run mutmut run 2>&1 | tee mutmut-run.log
run: nix develop --command bash -c "cd backend && mutmut run" 2>&1 | tee backend/mutmut-run.log

# 生存ミュータントの一覧(調査用)。run が途中で落ちても best-effort で出力する
- name: 生存ミュータント一覧を出力
working-directory: backend
continue-on-error: true
run: uv run mutmut results > mutmut-results.txt
run: nix develop --command bash -c "cd backend && mutmut results" > backend/mutmut-results.txt

- name: 結果集計
id: stats
working-directory: backend
run: |
# export-cicd-stats が mutants/mutmut-cicd-stats.json に統計を書き出す。
# score の定義(ADR-0017 / Stryker と同基準):
# killed = killed + timeout(無限ループ化もテストによる検出とみなす)
# survived = survived + no_tests(どのテストにも触れられない = 検出不能)
# score = killed * 100 / (killed + survived + suspicious + segfault)
uv run mutmut export-cicd-stats || true
STATS=mutants/mutmut-cicd-stats.json
nix develop --command bash -c "cd backend && mutmut export-cicd-stats" || true
STATS=backend/mutants/mutmut-cicd-stats.json
if [ -f "$STATS" ]; then
killed=$(jq '.killed + .timeout' "$STATS")
survived=$(jq '.survived + .no_tests' "$STATS")
Expand Down
Loading
Loading