diff --git a/.claude/rules/auth.md b/.claude/rules/auth.md new file mode 100644 index 000000000..1a330eb36 --- /dev/null +++ b/.claude/rules/auth.md @@ -0,0 +1,39 @@ +--- +description: Authentication rules +globs: + - 'src/lib/server/auth.ts' + - 'src/routes/(auth)/**' + - 'src/hooks.server.ts' +--- + +# Authentication + +## Lucia v2 + +- Session validation in `src/hooks.server.ts` +- Session data attached to `event.locals.user` +- User properties: `id`, `name`, `role`, `atcoder_name`, `is_validated` + +## Protected Routes + +- Validate `event.locals.user` in `+page.server.ts` load functions +- Redirect unauthenticated users to `/login` +- Validate `role` for admin-only routes + +## Form Validation + +- Use Superforms + Zod for auth forms +- Server-side validation is authoritative +- Return structured error responses + +## Key Files + +- `src/lib/server/auth.ts`: Lucia configuration +- `src/hooks.server.ts`: Global request handler +- `prisma/schema.prisma`: User, Session, Key models + +## Security + +- Never expose session secrets in client code +- Use HTTPS in production +- Validate all user inputs server-side diff --git a/.claude/rules/prisma-db.md b/.claude/rules/prisma-db.md new file mode 100644 index 000000000..b54f3612c --- /dev/null +++ b/.claude/rules/prisma-db.md @@ -0,0 +1,40 @@ +--- +description: Prisma and database rules +globs: + - 'prisma/**' + - 'src/lib/server/**' + - 'src/lib/services/**' +--- + +# Prisma & Database + +## Schema Changes + +1. Edit `prisma/schema.prisma` +2. Run `pnpm exec prisma migrate dev --name ` to create migration +3. Run `pnpm exec prisma generate` to update client (auto-runs after migrate) + +## Naming + +- Model names: `PascalCase` (e.g., `User`, `TaskAnswer`) +- Field names: `camelCase` (preferred) or `snake_case` (legacy) +- Relation fields: Descriptive names matching the relation + +## Key Models + +- `User`: User accounts with AtCoder validation status +- `Task`: Tasks with difficulty grades (Q11-D6) +- `TaskAnswer`: User submission status per task +- `WorkBook`: task collections +- `Tag` / `TaskTag`: task categorization + +## Server-Only Code + +- Import database client only in `src/lib/server/` +- Use `$lib/server/database` for Prisma client access +- Never import server code in client components + +## Transactions + +- Use `prisma.$transaction()` for multi-step operations +- Handle errors with try-catch and proper rollback diff --git a/.claude/rules/svelte-components.md b/.claude/rules/svelte-components.md new file mode 100644 index 000000000..099ffa3c2 --- /dev/null +++ b/.claude/rules/svelte-components.md @@ -0,0 +1,46 @@ +--- +description: Svelte component development rules +globs: + - 'src/**/*.svelte' + - 'src/lib/components/**' + - 'src/lib/stores/**/*.svelte.ts' +--- + +# Svelte Components + +## Runes Mode (Required) + +- Use `$props()` for component props +- Use `$state()` for reactive state +- Use `$derived()` for computed values +- Use `$effect()` for side effects + +## Props Pattern + +```svelte + +``` + +## Stores + +- Place store files in `src/lib/stores/` with `.svelte.ts` extension +- Use class-based stores with `$state()` for internal state +- Export singleton instances + +## Flowbite Svelte + +- Import components from `flowbite-svelte` +- Use Tailwind CSS v4 utility classes +- Dark mode: Use `dark:` prefix for dark mode variants + +## File Naming + +- Components: `PascalCase.svelte` +- Stores: `snake_case.svelte.ts` diff --git a/.claude/rules/testing.md b/.claude/rules/testing.md new file mode 100644 index 000000000..6e9f19554 --- /dev/null +++ b/.claude/rules/testing.md @@ -0,0 +1,54 @@ +--- +description: Testing rules and patterns +globs: + - '**/*.test.ts' + - '**/*.spec.ts' + - 'tests/**' + - 'src/test/**' +--- + +# Testing + +## Test Types + +| Type | Tool | Location | Run Command | +| ----------- | ---------- | ----------------------- | ----------------------- | +| Unit | Vitest | `src/test/**/*.test.ts` | `pnpm test:unit` | +| Integration | Vitest | `src/test/` | `pnpm test:unit` | +| E2E | Playwright | `tests/*.test.ts` | `pnpm test:integration` | + +## Unit Tests + +- Place tests in `src/test/` mirroring `src/lib/` structure +- Use `@quramy/prisma-fabbrica` for test data factories +- Mock external APIs with Nock + +## E2E Tests + +- Place in `tests/` directory +- Use Playwright test utilities +- Test user flows, not implementation details + +## Patterns + +```typescript +import { describe, test, expect, vi } from 'vitest'; + +describe('functionName', () => { + test('expects to do something', () => { + // Arrange + // Act + // Assert + }); +}); +``` + +## Coverage + +- Run `pnpm coverage` for coverage report +- Target: 80% lines, 70% branches + +## HTTP Mocking + +- Use Nock for mocking external HTTP calls +- See `src/test/lib/clients/` for examples diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ca9edd849..03603e46a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,10 @@ "workspaceFolder": "/usr/src/app", // Use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "dockerComposeFile": ["../compose.yaml"], - "mounts": ["source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,consistency=cached"], + "mounts": [ + "source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,consistency=cached", + "source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,consistency=cached" + ], // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // @@ -18,8 +21,11 @@ ], // "shutdownAction": "none", // + // Use 'initializeCommand' to run commands before the container is created. + "initializeCommand": "mkdir -p ~/.claude", + // // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "yarn install", + "postCreateCommand": "npm install -g @anthropic-ai/claude-code && pnpm install", // // Configure tool-specific properties. "customizations": { @@ -72,6 +78,7 @@ "svelte.enable-ts-plugin": true }, "extensions": [ + "anthropic.claude-code", "bradlc.vscode-tailwindcss", "christian-kohler.path-intellisense", "csstools.postcss", @@ -85,6 +92,10 @@ "vscode-icons-team.vscode-icons" ] } + }, + "containerEnv": { + "NODE_OPTIONS": "--max-old-space-size=4096 --dns-result-order=ipv4first", + "CLAUDE_CONFIG_DIR": "/home/node/.claude" } // // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..c971c5a09 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,59 @@ +# AtCoder NoviSteps + +A web service for tracking submissions on AtCoder and other competitive programming sites, which are graded by difficulty (Q11-D6). + +## Tech Stack + +SvelteKit 2 + Svelte 5 (Runes) + TypeScript | PostgreSQL + Prisma | Flowbite Svelte + Tailwind 4 | Vitest + Playwright + +## Commands + +```bash +pnpm dev # Start dev server (localhost:5174) +pnpm build # Build for production +pnpm test # Run all tests +pnpm test:unit # Vitest unit tests +pnpm test:integration # Playwright E2E tests +pnpm coverage # Report test coverage +pnpm lint # ESLint check +pnpm format # Prettier format +pnpm check # Svelte type check +pnpm exec prisma generate # Generate Prisma client +pnpm exec prisma migrate dev --name # Create migration (with description) +pnpm db:seed # Seed database +``` + +## Project Structure + +```md +src/routes/ # SvelteKit file-based routing +src/lib/ +├── actions/ # SvelteKit actions +├── clients/ # External API clients (AtCoder Problems, AOJ) +├── components/ # Svelte components +├── constants/ +├── server/ # Server-only (auth.ts, database.ts) +├── services/ # Business logic +├── stores/ # Svelte stores (.svelte.ts with Runes) +├── types/ # TypeScript types +├── utils/ # Pure utility functions +└── zod/ # Validation schemas +src/test/ # Unit tests (mirrors src/lib/) +tests/ # E2E tests (Playwright) +prisma/schema.prisma # Database schema +``` + +## Key Conventions + +- **Svelte 5 Runes**: Use `$props()`, `$state()`, `$derived()` in all new components +- **Server data**: `+page.server.ts` → `+page.svelte` via `data` prop +- **Forms**: Superforms + Zod validation +- **Tests**: Factories via `@quramy/prisma-fabbrica`, HTTP mocking via Nock +- **Naming**: `camelCase` variables, `PascalCase` types/components, `snake_case` files/routes, `kebab-case` directories +- **Pre-commit**: Lefthook runs Prettier + ESLint (bypass: `LEFTHOOK=0 git commit`) + +## References + +- See `package.json` for versions and scripts +- See `prisma/schema.prisma` for database models +- See `docs/guides/` for detailed documentation diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..8d78f5789 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,9 @@ +# CLAUDE.md + +@AGENTS.md + +## Claude Code Specific + +- Path-specific rules are in `.claude/rules/` +- Run `pnpm format` before committing +- When uncertain about project conventions, see existing code in `src/lib/` diff --git a/Dockerfile b/Dockerfile index 70c59f2b7..c8d2a968e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,6 @@ RUN apt-get update \ ENV NODE_PATH=/node_modules ENV PATH=$PATH:/node_modules/.bin -RUN pnpm install --frozen-lockfile +RUN pnpm install CMD ["pnpm", "dev"] diff --git a/docs/dev-notes/2026-02-08/introduce-claude-code/plan.md b/docs/dev-notes/2026-02-08/introduce-claude-code/plan.md new file mode 100644 index 000000000..44d3ab919 --- /dev/null +++ b/docs/dev-notes/2026-02-08/introduce-claude-code/plan.md @@ -0,0 +1,110 @@ +# DevContainer で Claude Code を利用するための設定変更 + +## Context + +DevContainer 内で Claude Code (CLI + VSCode 拡張) を利用したい。`pnpm install -g @anthropic-ai/claude-code` を追加するだけでは不十分であり、以下の対応が必要。ファイアウォールは導入せず、認証は `claude login` で行う。 + +## 「pnpm install -g だけで十分か?」: 不十分な理由 + +1. **VSCode 拡張が未追加** — エディタ上で Claude Code を使うには `anthropic.claude-code` 拡張が必要 +2. **設定の永続化がない** — コンテナ再構築のたびに認証情報・設定が消失する。`~/.claude` をボリュームマウントで永続化すべき +3. **メモリ不足のリスク** — Claude Code は大量メモリを消費。公式は `NODE_OPTIONS=--max-old-space-size=4096` を設定 +4. **インストール場所** — ルート `Dockerfile` は複数人で共有しており Claude Code を使わない開発者もいるため変更しない。`postCreateCommand` はコンテナ作成時のみ実行されるため devcontainer 側で制御するのに適切 +5. **npm vs pnpm** — グローバルインストールは `npm` が安定(公式も `npm` を使用) + +## 公式サンプルとの比較: 取り入れる / 取り入れない + +| 公式の要素 | 判断 | 理由 | +| ---------------------------------------- | -------- | -------------------------------------------------- | +| `anthropic.claude-code` 拡張 | **採用** | 必須 | +| `~/.claude` ボリュームマウント | **採用** | `claude login` の認証を永続化 | +| `CLAUDE_CONFIG_DIR` 環境変数 | **採用** | Claude Code が設定ディレクトリを認識 | +| `NODE_OPTIONS=--max-old-space-size=4096` | **採用** | メモリ不足防止 | +| `postCreateCommand` で `npm install -g` | **採用** | ルート Dockerfile を変更せず devcontainer 側で制御 | +| ファイアウォール (init-firewall.sh) | 不採用 | 個人開発では過剰 | +| zsh + powerline10k | 不採用 | 好みの問題。既に fish を導入済み | +| git-delta | 不採用 | Claude Code に直接関係なし | + +--- + +## 変更対象ファイルと内容 + +### 1. `.devcontainer/devcontainer.json` (修正) + +#### (a) postCreateCommand を追加 + +ルート `Dockerfile` は複数人で共有しているため変更しない。コンテナ作成時のみ実行される `postCreateCommand` で Claude Code をインストール: + +```json +"postCreateCommand": "npm install -g @anthropic-ai/claude-code" +``` + +#### (b) extensions に追加 + +```json +"anthropic.claude-code" +``` + +#### (c) mounts に追加(~/.claude をホストからバインドマウント) + +ホストの `~/.claude` をバインドマウントで共有。ホスト側で `claude login` した認証情報がコンテナ内でも使える。 + +```json +"source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind,consistency=cached" +``` + +> **当初は Docker ボリューム (`type=volume`) を使用していたが、コンテナ内での `claude login` が失敗するため変更した。** 詳細は「教訓」セクション参照。 + +#### (d) containerEnv を追加 + +compose.yaml の `environment` とは別に、devcontainer 固有の環境変数として設定: + +```json +"containerEnv": { + "NODE_OPTIONS": "--max-old-space-size=4096 --dns-result-order=ipv4first", + "CLAUDE_CONFIG_DIR": "/home/node/.claude" +} +``` + +- `--max-old-space-size=4096`: メモリ不足防止(既存の dev server 等にも影響するが、4096MB は開発環境として妥当) +- `--dns-result-order=ipv4first`: コンテナ内で認証が必要な場合の IPv4/IPv6 ミスマッチ対策 + +--- + +## 変更しないファイル + +- `Dockerfile` — 複数人で共有しているため変更しない。Claude Code のインストールは `postCreateCommand` で制御 +- `compose.yaml` — API キーの環境変数追加は不要(`claude login` で認証) +- `init-firewall.sh` — 作成しない(ファイアウォール不採用) + +--- + +## 認証手順(初回のみ) + +**ホスト(Mac ターミナル)で実行:** + +```bash +npm install -g @anthropic-ai/claude-code # 未インストールの場合 +claude login +``` + +ホストの `~/.claude` がバインドマウントでコンテナに共有されるため、コンテナ内での追加認証は不要。 + +## 検証方法 + +1. `Dev Containers: Rebuild Container` でコンテナをリビルド +2. ターミナルで `claude --version` → バージョンが表示されること +3. `claude login` で認証 +4. VSCode のアクティビティバーに Claude Code アイコンが表示されること +5. コンテナを再起動 → `claude --version` で設定が保持されていること + +## 教訓 + +- **CLI インストール ≠ 利用可能**: VSCode 拡張・設定永続化・メモリ設定が揃って初めて実用的になる。公式サンプルを事前に確認すべき +- **共有 Dockerfile に個人ツールを入れない**: `postCreateCommand` で devcontainer 利用者だけに閉じた制御ができる。`postCreateCommand` はコンテナ作成時のみ実行され、再起動のたびには走らない +- **公式サンプルは全部入りなので取捨選択が必要**: ファイアウォール・zsh・git-delta など、Claude Code の動作に直接関係しないものは省いてシンプルに保つ +- **コンテナ内での `claude login` は失敗しやすい**: OAuth コールバックがランダムポートを使うため、コンテナ→ホストのポート転送が間に合わない。さらに IPv4/IPv6 ミスマッチも起きる。対策として、ホストで認証し `~/.claude` をバインドマウントで共有する方式が確実 + +## 参考 + +- [Official settings - Claude Code](https://github.com/anthropics/claude-code/tree/main/.devcontainer) diff --git a/docs/dev-notes/2026-02-08/simplify-claude-md/plan.md b/docs/dev-notes/2026-02-08/simplify-claude-md/plan.md new file mode 100644 index 000000000..33cf14f94 --- /dev/null +++ b/docs/dev-notes/2026-02-08/simplify-claude-md/plan.md @@ -0,0 +1,179 @@ +# CLAUDE.md 簡素化計画 + +## 背景 + +現在の CLAUDE.md(約230行)は `/init` コマンドで生成されたもので、毎セッション読み込まれるためコンテキストを圧迫する。AGENTS.md のベストプラクティスや mizchi 氏のリポジトリ(67〜94行)を参考に、有用性を維持しつつコンテキスト消費を最小化するよう再構成する。 + +## 方針: AGENTS.md をメインに + +- **AGENTS.md**(約50行): Codex / Claude Code 共通のメイン指示ファイル +- **CLAUDE.md**(約10行): `@AGENTS.md` で import し、Claude 固有の設定のみ記載 +- **`.claude/rules/*.md`**: パス条件付きの詳細ルール(該当ファイル操作時のみ読み込み) + +この構成により: + +1. 単一の情報源(AGENTS.md)でマルチエージェント対応 +2. セッションごとのコンテキスト消費を最小化 +3. 詳細ガイダンスはパス条件でオンデマンド読み込み + +## 最終構成 + +```text +AGENTS.md # メイン(約50行)- Codex/Claude 共通 +CLAUDE.md # @AGENTS.md を import(約10行) +.claude/ +└── rules/ + ├── svelte-components.md # paths: ["src/**/*.svelte"] + ├── prisma-db.md # paths: ["prisma/**", "src/lib/server/**"] + ├── testing.md # paths: ["**/*.test.ts", "tests/**"] + └── auth.md # paths: ["src/lib/server/auth/**"] +.github/instructions/ # 削除(Copilot専用、情報が古い) +``` + +## AGENTS.md の内容(約50行) + +- プロジェクト概要(1〜2行) +- 技術スタック要約(1行) +- 主要コマンド(10行) +- プロジェクト構成(10行) +- 主要な規約(5行) +- 詳細は docs/package.json を参照 + +## .claude/rules/ のガイドライン + +- **各ファイル50行以内** +- **コード例は書かない** — 実際のソースファイルを参照 +- **命令形で具体的に** — 「$props() を使用する」のように +- YAML frontmatter でパス条件を指定 + +## 移行手順 + +1. AGENTS.md を新規作成(約50行) +2. CLAUDE.md を import のみに縮小(約10行) +3. .claude/rules/ に4ファイル作成 +4. .github/instructions/ を削除(8ファイル) +5. CONTRIBUTING.md に Codex 設定案内を追加予定(Codex 導入時に対応) + +## CLAUDE.md から削除する項目 + +- ゲストアカウント情報(`guest`/`Hell0Guest`)→ README.md のみに記載 +- 詳細なアーキテクチャ説明 → ソースコード参照 +- バージョン番号 → package.json を直接確認 +- コード例 → 実際のファイルを参照 + +--- + +## .github/instructions/ の有用コンテンツ分析 + +8ファイルを分析し、移管先を決定した。 + +### docs/guides/ に移管(人間向けドキュメント) + +分析の結果、3ファイルとも移管不要と判断した。 + +| 元ファイル | 当初の移管先 | 不要の理由 | +| ------------------------------ | --------------------------------------- | -------------------------------------------------------------------------- | +| source-code.instructions.md | `docs/guides/naming-conventions.md` | AGENTS.md の Key Conventions に記載済み | +| authentication.instructions.md | `docs/guides/security-checklist.md` | 旧ソースの認証フローが実態と乖離。`.claude/rules/auth.md` で基本カバー済み | +| docs.instructions.md | `docs/guides/documentation-strategy.md` | 参照される見込みが低く、保守コストに見合わない | + +### .claude/rules/ に圧縮して移管(エージェント向け) + +| 元ファイル | 有用なセクション | 移管先 | +| ----------------------------- | ---------------------------------- | ------------------------------------ | +| tests.instructions.md | テスト種別テーブル、カバレッジ目標 | `.claude/rules/testing.md` | +| ui-components.instructions.md | Svelte 5 Runes の使い方 | `.claude/rules/svelte-components.md` | + +### 削除(陳腐化 or 価値低い) + +| ファイル | 理由 | +| ------------------------------- | -------------------------------------------------------------------- | +| global.instructions.md | 設定ファイル名が古い(`.eslintrc.cjs` → 実際は `eslint.config.mjs`) | +| ci.instructions.md | 内容が薄い。`.github/workflows/` を見れば十分 | +| performance-seo.instructions.md | コード例が多いがソースと乖離リスク | +| ui-components.instructions.md | バージョンテーブルが古い(Tailwind 3.x → 実際は 4.x)、635行は長すぎ | +| authentication.instructions.md | 認証フロー図が実態と異なる(後述) | + +### 認証フロー図の調査結果 + +instructions に記載の認証フロー: + +```text +A[ユーザー登録] → B[AtCoder認証コード生成] → C[AtCoder側でコード確認] +→ D[認証ステータス更新] → E[セッション作成] → F[ログイン完了] +``` + +**実態**: + +| ステップ | 状態 | 根拠 | +| ------------------------ | ------------------- | ----------------------------------------------------------------------------------------- | +| A: ユーザー登録 | ✅ 機能 | Lucia v2 + Prisma で実装済み | +| B: AtCoder認証コード生成 | ⚠️ 実装済・UI非公開 | `validateApiService.generate()` 存在。ただし `AtCoderUserValidationForm` がコメントアウト | +| C: AtCoder側でコード確認 | ⚠️ 実装済・UI非公開 | `validateApiService.confirm()` 存在 | +| D: 認証ステータス更新 | ⚠️ 実装済・UI非公開 | `validateApiService.validate()` 存在 | +| E: セッション作成 | ✅ 機能 | Lucia v2 のセッション管理 | +| F: ログイン完了 | ✅ 機能 | `event.locals.user` にセット | + +B〜D は「回答状況が正しく取得されないバグ」のため UI を非公開にしている(`users/edit/+page.svelte` のコメント参照)。 + +**結論**: 認証フロー図は実態と異なるため、docs への移管は行わず削除する。 + +--- + +## Q&A まとめ + +### Q: 詳細ドキュメントを docs/guides/ に置くのはどうか? + +**A**: エージェントは docs/ を自動で読み込まない。AGENTS.md / CLAUDE.md / .claude/rules/ のみ自動読み込み対象。docs/ にエージェント向け指示を置いても効果がない。 + +### Q: Codex の Skills と Rules の違いは? + +**A**: + +- **Skills** = エージェントの能力を拡張(新しいワークフロー、スクリプト) +- **Rules** = コマンドのセキュリティ制御(allow/prompt/forbidden) + +Claude Code の `.claude/rules/` はこれらとは異なり、パス条件付きの指示ファイル。 + +### Q: .agents/skills/ を作成すべきか? + +**A**: 現時点では不要。Codex 導入時に再検討。 + +### Q: AGENTS.md を CLAUDE.md から import vs 内容を複製? + +**A**: `@AGENTS.md` で import。単一の情報源で保守性向上。 + +### Q: 命名規則の `I` prefix は使うか? + +**A**: 使わない。実際のコードベースで使用されておらず、モダン TypeScript では非推奨。 + +### Q: Core Web Vitals 目標値は残すか? + +**A**: 削除。performance-seo.instructions.md ごと削除。 + +### Q: 認証フロー図は docs に移管するか? + +**A**: 削除。A, E, F のみ機能しており、B〜D は UI 非公開のため図が実態と異なる。 + +--- + +## 参考資料 + +- [AGENTS.md](https://agents.md/) +- [Manage Claude's memory](https://code.claude.com/docs/en/memory) +- [Custom instructions with AGENTS.md](https://developers.openai.com/codex/guides/agents-md) +- [mizchi/playwright.mbt](https://github.com/mizchi/playwright.mbt/blob/main/AGENTS.md) +- [mizchi/luna.mbt](https://github.com/mizchi/luna.mbt/blob/main/CLAUDE.md) + +--- + +## 教訓 + +1. **AGENTS.md + CLAUDE.md の分離が効果的**: AGENTS.md(56行)をメインにし、CLAUDE.md(9行)は import のみにすることで、マルチエージェント対応と保守性向上を両立できた +2. **パス条件付きルールでオンデマンド読み込み**: `.claude/rules/` に分離することで、該当ファイル操作時のみ読み込まれ、コンテキスト消費を最小化 +3. **古いドキュメントは削除が正解**: `.github/instructions/` は設定ファイル名やバージョンが古く、実態と乖離していた。部分的な移管より全削除の方が安全 +4. **認証フロー図は実態と異なっていた**: ドキュメントの図と実装が乖離していたため、移管せず削除。コードが正しい。 +5. **コード例はドキュメントに書かない**: 実際のソースファイルを参照させる方が、乖離リスクがなく保守コストも低い +6. **Dev Container の bind mount は initializeCommand で安全に**: ホストに存在しないパスを bind mount すると Docker が root 所有で作成してしまう。`initializeCommand` でホスト側に事前作成することで権限問題を予防できる +7. **環境間の一貫性が重要**: Dockerfile と CI/CD で異なる `pnpm install` コマンド(`--frozen-lockfile` の有無)を使うと、開発環境と本番環境で動作が異なり開発者の混乱を招く。全環境で統一すること +8. **ボリュームマウント設定がインストール結果を隠す**: Docker Compose の bind mount(`./node_modules:/app/node_modules`)により、Dockerfile でインストールされた依存関係がホスト側のマウントで隠される。初回起動時には `postCreateCommand` で `pnpm install` を実行し、ホスト側に依存関係をインストールする必要がある