BluePeriod Docs
開発

開発ガイドライン

プロジェクトの開発品質と一貫性を維持するための規約集

開発ガイドライン (06_development_guidelines.md)

1. 概要

このドキュメントは、本プロジェクトにおける開発の品質と一貫性を維持するためのガイドラインを定めます。コードの可読性、保守性、拡張性を高め、チームメンバー間の協業を円滑にすることを目的とします。

1.1. パッケージマネージャーについて

本プロジェクトはBunを使用しています。npmではありません。

  • インストール: bun install(作業対象のサブプロジェクトディレクトリで実行。ルートのbun installはルートのdevDependenciesのみ対象)
  • スクリプト実行: bun <script> または bun run <script>build, testは組み込みコマンドと競合するため bun run を使用)
  • 依存関係追加: bun add <package>(対象サブプロジェクトディレクトリで実行)
  • 開発サーバー: bun next

2026年3月にnpmからBunに移行しました。詳細は 02_technology_stack.md を参照してください。

1.2. スクリプト実行時の注意

Bunの組み込みコマンド(build, test)と同名のnpmスクリプトが競合する場合、bun <script> は組み込みコマンドを優先します。npmスクリプトを実意で呼び出す場合は bun run <script> としてください。

コマンド挙動正しい呼び出し
bun buildBunのバンドラが起動(bun run build
bun testBunのテストランナーが起動(非推奨vi.hoisted などのVitest APIに非対応)bun run test または bun next:test

1.3. ルートの集約スクリプト

プロジェクトルートの package.json に、全ワークスペースを横断する集約スクリプトを定義しています。

コマンド内容対象
bun all:check全ワークスペースのlint + typechecknext, fuma, astro, tauri
bun all:build全ワークスペースのビルドnext, fuma, astro(Tauri除外)
bun all:prod全ワークスペースのVercelデプロイnext, fuma, astro(Tauri除外)
bun all:fix全ワークスペースのlint + format自動修正next, fuma, astro

各サブコマンド(bun next:check, bun fuma:build 等)も個別に実行可能です。

注意: bun all:check は全サブプロジェクトを横断するため、各サブプロジェクト単位での実行(bun next:check等)も推奨します。

2. コーディング規約

2.1. 基本方針

  • リンター・フォーマッター: Biome v2.4.14 を利用して静的解析とコードフォーマットを統合的に行います。Rust製で高速に動作します。
  • 設定: biome.jsonc でlint・formatの全設定を一元管理しています。CSSパーサーでは css.parser.tailwindDirectives を有効化し、Astroファイルは experimentalFullSupportEnabled でサポートしています。
  • 実行: 開発中はエディタのBiomeプラグインでリアルタイムに問題を検出し、コミット前にはbun all:checkコマンドでコード全体の検証を行ってください。

2.2. 命名規則

  • 変数・関数: camelCase (例: fetchData, userName)
  • コンポーネント: PascalCase (例: ChatView, MainPanel)
  • 型・インターフェース: PascalCase (例: ProjectRecord, ChatMessage)
  • ファイル名:
    • Reactコンポーネント: PascalCase.tsx (例: ChatView.tsx)
    • それ以外のTypeScriptファイル: kebab-case.ts (例: project-atoms.ts)
  • ストレージキー (localStorage/Atoms):
    • フォーマット: bp:<category>:<key-name>
    • 規則: すべて小文字、単語区切りはハイフン (kebab-case)、カテゴリ区切りはコロン (:)。
    • 例: bp:ui:active-project-id, bp:sync:last-synced-at
    • 詳細については /doc/log/proposals/2026/01/2026-01-12_1210_proposal_storage-key-naming-convention.md を参照。

2.3. コンポーネント設計

  • 関心事の分離: UIコンポーネントは状態管理やビジネスロジックから分離し、可能な限りPureに保ちます。ロジックはカスタムフック (/hooks) やJotaiのAtom (/stores) にカプセル化します。
  • 粒度: 巨大なコンポーネントは避け、意味のある単位で小さなコンポーネントに分割します。
  • UIライブラリ: shadcn/uiをベースとして使用します。既存のコンポーネントを積極的に再利用し、新しいコンポーネントを作成する際は既存の設計思想を踏襲してください。
  • アイコン: Lucide React を使用します。UIでアイコンを使用する際は、絵文字や画像ファイルではなく、原則としてLucide Reactのコンポーネントを利用してください。
  • ローディングUI: データ読み込みなど、非同期処理が発生する箇所では必ずローディング状態を示すUIを実装してください。
    • 優先度 高: shadcn/uiのSkeletonコンポーネントを使用し、コンテンツのレイアウトを維持したままローディング状態を示します。
    • 優先度 中: スピナーなど、他のローディング表現も適宜使用可能ですが、ユーザー体験の観点から可能な限りSkeletonの使用を検討してください。
  • 一貫したスタイリング: カラーやスペーシングは、必ず 09_design_system.md で定義されたセマンティックトークンを使用してください。ハードコードされた即値の使用や、Raw Colorの使用は禁止です。

2.4. 状態管理 (Jotai)

  • Atomの分割: Atomはドメインごと、または機能ごとにファイルを分割して管理します (/storesディレクトリ内)。
  • スコープ: Atomは可能な限り最小のスコープで定義し、グローバルな状態を不必要に増やさないように注意します。
  • 命名: Atom変数には ...Atom という接尾辞をつけます (例: projectDataAtom)。
  • タイムゾーン: アプリ全体の時間概念はブラウザのタイムゾーン設定を唯一の情報源とします。タイムゾーン情報が必要な場合は getBrowserTimeZone()@/lib/chat/dateUtils)を使用し、独自のタイムゾーン設定Atomを作成してはいけません。AIへの時間コンテキスト注入には getCurrentTimestampForAI() / formatTimestampForAI を経由してください。

2.5. バックエンド通信 (Supabase)

Supabaseとの通信には、Next.jsの実行コンテキストに応じて適切なクライアントを使用する必要があります。

  • クライアントコンポーネント ('use client') での使用:

    • ファイル: src/lib/supabase/client.ts
    • 概要: ブラウザ環境でのみ安全に動作するクライアントです。Reactのフック(useState, useEffectなど)を持つコンポーネント内で使用してください。
  • サーバーコンポーネント, Server Actions, Route Handlersでの使用:

    • ファイル: src/lib/supabase/server.ts
    • 概要: サーバーサイドでのみ動作するクライアントです。Cookieの読み書きを伴うため、ブラウザ環境では動作しません。非同期処理やデータフェッチを行うサーバーコンポーネント、フォーム送信を処理するServer Actions、APIエンドポイントを定義するRoute Handlers内で使用してください。

2.6. データベース型定義の生成

Supabaseのデータベーススキーマと連動したTypeScriptの型定義は、以下のコマンドで生成・更新できます。

# next-appディレクトリで実行
bun gen:types

重要:

  • データベースのテーブルやカラムに変更を加えた後は、必ずこのコマンドを実行して next-app/src/lib/types/supabase.ts を最新の状態に更新してください。
  • このコマンドは、Windows環境で発生しがちなファイルの文字化け問題を回避する設定を含んでいます。手動で bunx supabase gen types... を実行すると問題が再発する可能性があるため、必ず bun gen:types を使用してください。

2.7. Hono API の開発

API の実装には Hono を使用し、モジュール化された構造とプラットフォーム非依存のロジックを維持してください。

  • エントリポイント: すべての API リクエストは app/api/[[...route]]/route.ts を経由します。
    • 重要: route.ts では必ず GET, POST, PUT, DELETE, PATCH すべてのメソッドを handle(app) でエクスポートしてください。エクスポート漏れがあると 405 Method Not Allowed エラーが発生します。
  • モジュール分割: 各機能(例: stripe, infra, auth, projects, library, publish)は src/server/api/ 内に個別の Hono インスタンスとして作成し、メインアプリに api.route('/path', module) でマウントします。
    • ルーティングの注意 (trailing slash): Hono のサブアプリ・マウント形式では、/api/library(末尾なし)と /api/library/(末尾あり)の挙動が異なる場合があります。
      • サブアプリ内で .get('/', ...) と定義している場合、マウントポイントの末尾スラッシュがないリクエスト(/api/library)は「空文字」として扱われ、マッチしません。
      • 対策として、共通のハンドラを定義し library.get('/', handler)library.get('', handler) (空文字) の両方を登録するか、リダイレクトを活用してください。
  • ロジックの分離 (Service Layer パターン):
    • API ハンドラ(Controller)内にはビジネスロジックを直接記述しないでください。
    • すべてのロジック(DB操作、外部通信、データ変換)は src/lib/features/<feature-name>/service.ts に抽出します。
    • API ハンドラは、リクエストの検証(Zod)、サービスの呼び出し、レスポンスの返却のみを担当します。これにより、同じロジックをデスクトップ版など他の環境でも再利用可能になります。
  • データアクセスの原則(Feature-Root Architecture):
    • データアクセスとビジネスロジックは Service 層に集約し、すべてのインターフェース(GUI・LangChain内製エージェント・MCP外部エージェント)が Service を経由してデータにアクセスします。
    • 新規コードでは Service/Atom 経由のアクセスを必須とします。 コンポーネントやMCPブリッジから db.* を直接呼び出さないでください。
    • 既存の直接DB呼び出しは技術的負債として段階的にService経由に移行します。
    • MCPブリッジ(useMcpBridge.ts)はビジネスロジックを記述せず、Service の呼び出しのみを行うディスパッチャとして実装してください。
  • リクエストパラメータの柔軟性:
    • 実装の際は、フロントエンドが GET/DELETE でクエリパラメータを使う場合と、POST/PATCH で JSON ボディを使う場合の両方を考慮し、柔軟に取得できるようにしてください(例: c.req.query('id') || (await c.req.json()).id)。
  • 型安全性 (バリデーション):
    • リクエストの検証には @hono/zod-validator を使用してください。
    • zod のバージョンは、プロジェクト全体の型不整合を避けるため 4.1.12 に固定して使用してください。
  • ランタイム: Vercel の関数制限を回避するため、原則として Edge Runtime (export const runtime = 'edge') で動作するように実装してください。
    • Edge 環境では supabase.auth.getSession() ではなく supabase.auth.getUser() を使用して安全にユーザー認証を行ってください。
  • 型安全性 (RPC): クライアント側で型安全な通信を行うため、AppType をエクスポートし、Hono Client (hono/client) を利用可能な状態に保ってください。

2.8. 翻訳(i18n)

実装時には、必ずi18n準拠でのUI実装をおこないます。 日本語でテキストをハードコードするなどは厳禁です。 ただし、そこまで重要でないテキストの場合は、英語でとりあえずハードコードすることは許可します。

2.8.1. i18next-cliによる翻訳管理

本プロジェクトでは、i18next-cliを使用した自動化された翻訳キー管理ワークフローを採用しています。

基本的な開発フロー:

  1. 新しいキーの追加: コンポーネント内で t('new.key') を使用
  2. 抽出: cd next-app && bun i18n:extract で翻訳ファイルを更新
  3. 翻訳: 各言語の src/i18n/locales/{lang}/translation.json に翻訳を追加
  4. 型生成: bun i18n:types で型定義を更新(必要に応じて)

主なコマンド:

コマンド説明
bun i18n:extractソースコードから翻訳キーを抽出し、JSONを更新
bun i18n:typesTypeScript型定義を生成
bun i18n:status翻訳状況を確認(現在は100%達成済み)
bun i18n:lintハードコードされた文字列を検出
bun i18n:ciCI用抽出(変更時に失敗)

動的キーの使用:

変数経由でキーを参照する場合(t(variable))、i18next.config.tspreservePatterns にパターンを追加してください。

preservePatterns: [
  'agent.tools.*.*',  // agent.tools.<tool_id>.label / .description を保護
]

詳細については: 07_i18n.md を参照してください。

2.9. プロットデータの多言語表示 (i18n)

プロット構造(Part, Chapter, Story等)を表示する際は、単に title フィールドを表示するのではなく、plotTranslations データの有無を確認し、現在の表示言語設定に応じた翻訳タイトルを優先して表示するように実装してください。

  • 参照: TableOfContents.tsx における getTranslatedTitle の実装パターン。
  • 言語コードの扱い: en-US 等のロケールではなく、ベースとなる言語コード (en, ja) をキーとして使用してください。

2.10. 画像データの取り扱い

本プロジェクトでは、キャラクターアイコンや作品の表紙画像をブラウザの IndexedDB に Base64 形式で保存します。ストレージ容量の節約と同期パフォーマンス向上のため、以下のルールを遵守してください。

  • 自動圧縮の適用: 画像を保存する前に、必ずリサイズと圧縮を行ってください。
  • 共有ユーティリティの利用: @/lib/image-processing にある processImageAsDataUrl を使用して、一貫した圧縮処理(WebP形式、適切な解像度制限)を適用してください。
  • 推奨設定:
    • キャラクターアイコン: 最大 512px
    • 作品表紙: 最大 1024px (アスペクト比 1:1.6 推奨)
    • 品質設定: quality: 0.8 程度

2.11. AIエージェント実装時の注意点

  • コンテキストの疎結合化: ツール実行の結果や大規模な生成データを、そのまま会話履歴(Context)に埋め込まないでください。コンテキストウィンドウの鮮度を保つことが最優先です。
  • サマリーの活用: src/lib/chat/formatters.tsformatMessageWithThoughts 等のフォーマッタレベルで、長大なツール引数や出力は自動的にサマリー(文字数など)に変換されるように実装してください。
  • 外部メモリへの委譲: 大規模な成果物は Session Artifacts として外部(DB)に保存し、AIには必要な時だけ read ツールで明示的に取得させるように設計してください。
  • 表示と実態の同期: UIで「折りたたまれている」詳細は、モデルのコンテキスト内でも要約されるべきです。詳細は 12_ai_agent_architecture.md を参照してください。

3. Git運用ルール

3.1. ブランチ戦略

GitHub Flow を採用します。

  1. mainブランチ: 現在、諸事情につき、mainはつかっておりません

  2. devブランチ: 実質的にメインブランチです。常にデプロイ可能な安定した状態を保ちます。直接のコミットは基本的には禁止です。

  3. フィーチャーブランチ:

    • 新機能開発、バグ修正、リファクタリングなど、全ての作業はdevブランチから新しいブランチを作成して行います。
    • ブランチ名は、その目的が分かりやすいように命名します。
      • feat/... (新機能)
      • fix/... (バグ修正)
      • refactor/... (リファクタリング)
      • docs/... (ドキュメント)
      • 例: feat/add-chat-character-edit-function

※ここは現在のところOFFにしてありますので無視して構いません。 {/* 3. プルリクエスト (Pull Request): - 作業が完了したら、mainブランチへのマージをリクエストするプルリクエストを作成します。 - プルリクエストには、変更の概要、目的、関連するIssueなどを記述します。 - コードレビューが完了し、CI(継続的インテグレーション)のチェックが通るまでマージは行いません。 */}

3.2. コミットメッセージ規約

Conventional Commits 規約に準拠します。これにより、コミット履歴の可読性が向上し、将来的なリリースノートの自動生成も可能になります。

  • フォーマット:

    <type>(<scope>): <subject>
    
    <body>
    
    <footer>
  • typeの種類:

    • feat: 新機能の追加
    • fix: バグ修正
    • docs: ドキュメントのみの変更
    • style: コードの動作に影響しない、フォーマットなどの変更
    • refactor: バグ修正や機能追加ではないコードの変更
    • perf: パフォーマンスを向上させるコードの変更
    • test: テストの追加・修正
    • chore: ビルドプロセスや補助ツールの変更
  • scope (任意): 変更が影響する範囲 (例: chat, db, api)

  • subject (必須): 変更内容の簡潔な説明 (50文字以内)

  • 言語: コミットメッセージの本文およびタイトルは、原則として英語で記述してください。これにより、グローバルな開発における文脈理解を迅速化します。

  • ※コードブロックで囲う必要はありません。

  • concrete example:

    feat(chat): ストリーミングレスポンスのサポートを追加
    fix(db): chatMessagesテーブルのインデックススキーマを修正
    
    以前のスキーマでは大量のチャット履歴をクエリする際にパフォーマンスが低下していました。
    この変更により、複合インデックスを追加してパフォーマンスを向上させました。

4. AIアシスタントのための実装前チェックリスト

AIアシスタントがタスクを遂行する際、以下の項目を必ずセルフチェックしてください。

  • ガイドラインの再読: 06_development_guidelines.md を最新の状態まで読んだか?
  • 思想の再確認: すべての根幹となる 00_philosophies_of_blueperiod を読み、特に「深いエージェント」としての振る舞いを理解しているか?
  • デザインシステムの確認: UI変更を伴う場合、09_design_system.md を読んだか?
  • セマンティックトークンの使用: ハードコードされたRaw Color (bg-blue-500等) を使用せず、bg-primary等のトークンを使用しているか?
  • 命名規則の遵守: ファイル名、関数名、Atom名、ストレージキー名が規約通りか?
  • i18nの徹底: テキストをハードコードせず、ja.json / en.json に追加して t() 関数を使用しているか?
  • 影響分析: 変更が他のコンポーネントやAtomの挙動に悪影響を与えないか?
  • IndexedDBの日付ガード: データベースへの保存や同期データの復元時に、@/lib/utils/dateensureDate を使用して日付オブジェクトを保護しているか?(標準の Date 以外は索引されないため)
    • 重要: ensureDateDate 型を期待しています。テーブルのTypeScript型定義(lib/types.ts)で createdAt: Date と宣言している場合、値の生成時も必ず new Date() を使用し、Date.now()(number)を使用しないこと。型と実値のミスマッチは ensureDate のパースエラーとクラッシュなき日時すり替えを引き起こします。
    • 型定義の一貫性ルール: Dexieのテーブル定義(lib/db.ts のインライン型)と lib/types.ts の型定義は、日付フィールドの型が必ず一致していることを確認すること。どちらか一方で number、他方で Date という混在は厳禁。
  • Message Normalization: LLM API 呼び出しの直前で normalizeMessages を適用し、System Prompt の順序や重複を最適化しているか?
  • AI Temporal Context: getBrowserTimeZone() でブラウザのタイムゾーンを取得し、getCurrentTimestampForAI を使用してシステムステータスに現在時刻(タイムゾーン付き)を含めているか? また、メッセージ履歴に formatTimestampForAI 等を用いて時間情報を付与しているか?
  • テスト規約の確認: 36_testing_strategy を読み、新規・変更したServiceメソッドやWrite-only Atomに対してテストを作成しているか?
  • Biomeフォーマットの実行: AIアシスタントがCLIからファイルを編集した場合、IDEの保存時フックが発火しないため、コード変更の完了後に必ず bun all:fix を実行すること。これを怠るとフォーマット不整合が発生する。

4.1. Gitワークツリーを使った並列開発

機能開発をGitワークツリー(git worktree add)で並列して行う場合の規約です。

ワークツリーの配置

  • ワークツリーはプロジェクトルートの worktrees/<branch-name>/ に配置する
  • worktrees/.gitignore に追加し、メインリポジトリの管理対象外とする

ドキュメント整備のルール

  • ワークツリー内で作業中は、Issue/Plan/Reportなどのドキュメントもワークツリー側に作成する
  • ドキュメントの作成場所: worktrees/<branch-name>/doc/log/ 配下
  • メインリポジトリ側の doc/log/ は直接編集しない(コンフリクト防止)
  • ブランチのマージ時に、ドキュメントもあわせて統合する
  • Fumadocs等のシステムドキュメントfumadocs/content/docs/)についても、ワークツリー側で編集する

開発サーバーのポート

  • メインリポジトリとワークツリーで同じポートを使用すると競合する
  • ワークツリー側の package.jsondev スクリプトに --port 0 を指定し、自動割り当てにする
  • マージ前に忘れずに元のポート設定に戻す

マージ時の確認事項

  • ドキュメントの整合性(ワークツリー側の docs をメインに統合)
  • ポート設定の復元(--port 0 を削除)
  • worktrees/ 配下のクリーンアップ(git worktree remove

5. Hono & Edge API 開発ガイドライン

Vercel Edge Runtime 上で Hono を用いて API を開発する際は、以下の点に留意してください。

5.1. ランタイムとポータビリティ

  • Runtime 明示: ファイル先頭に export const runtime = 'edge'; を記述してください。
  • Service Layer の分離: ビジネスロジックは Hono のハンドラに直接書かず、src/lib/features/ 配下のサービス関数として抽出してください。

5.2. セキュリティと認証

  • Auth: Hono ミドルウェアで検証済みのユーザー情報を c.get('user') で取得して使用してください。
  • getUser: セッション偽装を防ぐため、常に supabase.auth.getUser() またはミドルウェアの検証結果を信頼してください。

5.3. 型定義とバリデーション

  • Zod: リクエストボディやクエリパラメータの検証には zValidator を使用し、型安全性を確保してください。
  • any の禁止: API の境界では特に厳格に型を定義し、可能な限り any を使用しないでください。

5.4. Trailing Slash

  • Hono のメインアプリケーション([[...route]]/route.ts)で { strict: false } を設定しています。これにより、/api/path/api/path/ の両方が同一エンドポイントとして処理されます。各サブモジュール側で個別にリダイレクトや重複定義を行う必要はありません。

5.5. バリデーションとエラーハンドリングの標準化

  • 共有ユーティリティ: src/server/api/common.tsvalidationHookerrorResponse を必ず使用してください。
  • バリデーション: zValidator の第3引数として validationHook を渡すことで、フロントエンドに対して「どのフィールドがどのように不正か」を詳細に含む 400 Bad Request を自動返却します。
  • 内部エラー: try-catchcatch ブロックでは return errorResponse.internalError(c, error) を使用してください。これにより、適切なログ出力と 500 レスポンスが統一された形式で行われます。
  • 認証エラー: 認証が必要なエンドポイントでは return errorResponse.unauthorized(c) を使用してください。

関連ドキュメント:

On this page