diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 28df89b..91e032d 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -51,4 +51,4 @@ ## 属性 -この行動規範は[Contributor Covenant](https://www.contributor-covenant.org)、バージョン 2.0、(https://www.contributor-covenant.org/version/2/0/code_of_conduct.html)から適応されました。 +この行動規範は[Contributor Covenant](https://www.contributor-covenant.org)、バージョン 2.0、()から適応されました。 diff --git a/.github/ISSUE_TEMPLATE/design_upstream_task.md b/.github/ISSUE_TEMPLATE/design_upstream_task.md new file mode 100644 index 0000000..33f52c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/design_upstream_task.md @@ -0,0 +1,317 @@ +--- +name: NextJS画面設計イシューテンプレート(Upstream Demo用) +about: NextJS画面設計向けのイシューテンプレートです +title: "[DESIGN] Upstream Demo: ★ここに画面名★" +--- + + + +# [DESIGN] Upstream Demo: ★ここに画面名★画面 + +## 目的 + +Upstream(public) デモアプリの画面設計を作成する。 +本プロジェクトは Dependency Inversion Principle に基づき、 Composition Root(AppProvider)で依存を解決する Plugin 型アーキテクチャを採用する。 +このIssueの目的は「設計内容を製造Agentへ漏れなく引き継ぐこと」であり、実装そのものは行わない。 + +以下を SSOT として固定する。 + +* Plugin型(DIP): 依存解決は Composition Root(AppProvider)のみ +* ページ設計は contracts(契約)/ public UI / AppContext の責務分離で構成する + +## 成果物 +- `.github/copilot/80-templates/implementation-plan.md` に準拠した plan ドキュメントを`.github/copilot/plans/-page-.md`として作成する。 +- ファイル追加は、`.github/copilot/plans/-page-.md`のみとする。 +- コード修正・他のファイルの追加・編集を禁止する! + +## 前提 / スコープ + +* Upstream(public) のみ(private の存在/実装は一切書かない) +* ルーティングは App Router(**app/** 方式)のみを採用する +* モックデータは可(実装Issueで publicデモとして動く完成実装にする) + +### モック画面 + +モック画面 `` を参考に対象画面を設計する。 + +対象画面は下記の部品を持つ。 + +* `<部品名>` +* `<部品名>` +* `<部品名>` +* `<部品名>` +* `<部品名>` +* `<部品名>` + +## ゴール(このIssueで達成) + +1. 「ページを追加する手順」が **SSOT化**されている +2. contracts と UI と DI が **規約通りに分離**されている +3. `.github/copilot/plans/-page-.md` の機能設計書を新規作成している + +## 非ゴール + +* private 実装、DB接続、認証、社内API等の導入 + +## SSOT規範(必須) + +### Dependency Injection Rule + +* 依存性注入(依存解決)は `AppProvider.tsx` のみ +* `app/*/page.tsx` は **`packages/contracts/*` と `packages/ui/*` と AppContext** を参照可能とする +* `packages/contracts/*` は interface/type のみ(実装、URL、認証、fetchなどの具体語禁止) +* `packages/ui/*` は public UI(会社固有前提なし) + +### CSSフレームワーク + +* MUI は Emotion エンジンを前提に運用する(推奨) +* styled は @emotion/styled を標準とする(MUIと同一基盤) +* Tailwind はユーティリティ(レイアウト/微調整/状態/レスポンシブ)用途に限定する +* スタイル注入順を固定し、Tailwind による上書きを容易にする + +#### CSS必須ルール + +- **スタイル注入順を固定する**:MUIのスタイルは先に注入し、Tailwind等で上書き可能にする(`StyledEngineProvider injectFirst`) +- **同一要素で同じプロパティを多重指定しない**(例:padding/color/fontをTailwindとMUIとstyledで混ぜない) + +#### CSS原則 + +- MUIを `styled-components` エンジンへ切替(`@mui/styled-engine-sc`)は原則しない(SSR差分/依存/事故率が上がるため) + +## フォルダ構造(Upstream) + +* Next.js アプリのルートは repo 直下で固定。だけど UI/Contracts は同一repo内のローカルパッケージとして `packages/` に切る。 +* 本repoは「`apps/` 配下にアプリを置く monorepo 形」は採用しない(=アプリは repo 直下)。ただし `packages/` にローカルパッケージ(`ui` / `contracts` / `plugins`)を同居させる。 +* `src/providers` はアプリ全体(Root layout 相当)の Provider 群、`app/**/providers.tsx` は画面スコープの Provider とする(Composition Root としての依存解決は `AppProvider.tsx` のみ)。 +* UI はアトミックデザインを採用する。 + + * Hooks は Organisms 以上(または Container)で許可。 + * Atoms/Molecules は基本ダム。状態は持っても「見た目に閉じる」。 + * 画面スコープの状態共有は Page/Template 配下の Context を第一選択。 + * Props drilling が 3 階層を超えたら Context or Container を検討。 +* ルーティングは App Router(`app/` 方式)とする。 + + * `next/router` は使わず `next/navigation` を使う。 + * Server Components がデフォルトで、クライアントが必要なコンポーネントだけ "use client" を付ける。 + +[見本] + +``` +app/ + layout.tsx # Root layout(Server) + page.tsx # /(Server) + / + page.tsx # /(Server) + layout.tsx # / 配下のレイアウト + providers.tsx # (use client) 画面スコープContext Provider(依存解決はしない) + _components/ # ルート専用の薄い部品 + DashboardShell.tsx # (use client) state/compose only + +public/ + +src/ + providers/ + AppProvider.tsx # (use client) アプリ全体のProvider + AppContext.tsx + composition/ + createClientContainer.ts + createServerContainer.ts + registerPlugins.ts + pluginRegistry.ts + lib/ + createClientDeps.ts # Public deps factory(※containerを使うなら薄くする/廃止も可) + createServerDeps.ts # Server deps factory(※containerを使うなら薄くする/廃止も可) + +packages/ + contracts/ + src/ + index.ts # packages/contracts の公開API(barrel) + domain/ # ドメインモデル(Entity/ValueObject など、UIやI/Oに依存しない) + Project.ts # ドメイン型: Project + Session.ts # ドメイン型: Session + ports/ # 抽象(DIPの境界)。plugins がここを実装する + ProjectRepository.ts # Project 永続化/取得の抽象 + SessionRepository.ts # Session 永続化/取得の抽象 + Telemetry.ts # ログ/計測の抽象 + Auth.ts # 認証/認可の抽象(例: 現在ユーザー取得、権限判定) + usecases/ # ユースケース契約(アプリの操作単位)。container から呼ばれる + SearchProjects.ts # ユースケース: Project 検索 + GetProject.ts # ユースケース: Project 取得 + GetSession.ts # ユースケース: Session 取得 + dto/ # 表示/転送向けのDTO(ドメインと分離したい場合) + ProjectDto.ts # DTO: Project 表示/転送用 + pages/ + .ts # ページ単位の契約(Route単位) + + ui/ + src/ + pages/ # 見た目のページ(App Routerのpage.tsxとは別)/画面スコープのContext(フィルタ状態や選択状態など)を提供 + Page/ + Page.tsx + atoms/ # state を持つ場合も UI状態(hover, open等)のみとする/ドメイン状態・データ取得・副作用は禁止 + ProjectName/ + ProjectName.tsx + templates/ # レイアウト構造(Header/Footer/サイドバー配置など)/画面共通のContextはtemplatesで提供 + StandardLayout/ + StandardLayout.tsx + molecules/ # 簡単な UI state は可、ただし外部I/Oやグローバル状態は避ける + ProjectCard/ + ProjectCard.tsx + organisms/ # hooks を許可(フォーム、リスト、フィルタ状態など)/ただし「ドメインの取得・永続化」は、可能なら usecase 呼び出し(container経由)に寄せる + Header/ + Header.tsx + ProjectCardList/ + ProjectCardList.tsx + Footer/ + Footer.tsx + + plugins/ + src/ + index.ts + http/ + projectRepository.ts + sessionRepository.ts + storage/ + cookieSessionStore.ts + telemetry/ + consoleTelemetry.ts + auth/ + noopAuth.ts +``` + +※上記のフォルダ構造例は「リポジトリルート」直下を前提としており、`app/` ディレクトリはリポジトリ直下に配置される。 +### ディレクトリの責務 + +#### `app/`(ルーティング:薄く) + +* URL の入口(`page.tsx` / `layout.tsx`)だけを持つ。 +* **実装詳細(HTTP/DB/Storage)を直に `new` しない。** +* ルートは「container(DIコンテナ)から usecase を呼ぶ」くらいに留める。 + +例: + +* `app//page.tsx` は `Page`(UI)に props を渡す or サーバでデータ取得して渡す。 + +#### `src/composition/`(依存生成の中心) + +* 依存(ports の実装)を組み立て、usecase を生成し、必要なら plugin を登録する(ここで生成した依存を `AppProvider.tsx` から利用する)。 +* App Router の Server/Client の違いに合わせて入口を分ける。 + +推奨ファイル: + +* `createClientContainer.ts`:ブラウザ実行("use client" 側)で使う依存を束ねる。 +* `createServerContainer.ts`:サーバ実行で使う依存を束ねる(秘密情報・サーバ専用 OK)。 +* `registerPlugins.ts`:どの plugin 実装を採用するか(差し替え点)。 +* `pluginRegistry.ts`:plugin の登録/参照の器。 + +#### `src/providers/AppProvider.tsx`(Client Composition Root) + +* React Context などで container をアプリに流す。 +* ここが「Provider としての Composition Root(AppProvider)」の本体。 + +分業の明確化: + +* `src/composition` は **container 生成(依存生成)** +* `src/providers` は **container を React ツリーへ注入(Provider)** + +### plugins パッケージ(実装の差し替え:必須) + +#### `packages/plugins/` + +* `contracts/ports` を満たす実装群。 +* 例:HTTP 版 repo、cookie 版 session、console telemetry など。 +* upstream ではデフォ実装をここに置き、downstream で差し替える方針とも相性が良い。 + +`packages/plugins/` は必須とする。実装の差し替え点は原則ここに集約し、採用と切替は `src/composition/registerPlugins.ts` で確定する。 + + +### Atomic Design(ui) + +#### `packages/ui/` + +* Atoms/Molecules:基本ダム(状態は「見た目に閉じる」)。 +* Organisms 以上(or Container)で hooks / 状態 / Context 利用。 +* Templates:レイアウト構造。 +* Pages:画面の見た目のまとまり(App Router の `page.tsx` とは別)。 + +**呼び出し責務の固定**: + +* usecase を呼ぶのは原則 `app/` or `src/*`(container 側) +* `packages/ui` は props を受けて描画に専念(Atoms/Molecules の原則と整合) + + +### contracts の方針(ドメイン/ユースケース単位が一次) + +#### `packages/contracts/src/domain/` + +* ドメインモデル(Entity/ValueObject 的な型)。 +* 例:`Project`, `Session`。 + +#### `packages/contracts/src/ports/` + +* DIP の「抽象(依存の向きの逆転)」。 +* 例:`ProjectRepository`, `SessionRepository`, `Telemetry`。 + +#### `packages/contracts/src/usecases/` + +* ユースケース単位の契約(再利用の核)。 +* 例:`SearchProjects`, `GetProject`, `GetSession`。 +* UI(アプリ側の container)はこの usecase 契約を呼ぶ(または adapter 経由)。 + +#### `packages/contracts/src/dto/`(任意) + +* 表示用/転送用の DTO(ドメインと分離したい場合)。 + +> ページ単位の contracts は「必須」ではなく、必要が出たら **composition** として追加する位置づけが安全。 +> 例:`contracts/src/pages/.ts` は、`SearchProjects` 等を束ねるだけの薄い合成契約にする。 + + +## 型定義(SSOTとして固定) + +各ページは実装時に次の 4 点セットを基本とする(本Issueでは追加せず、planに定義する)。 + +1. docs: `.github/copilot/plans/-page-.md`(ページ仕様) +2. contracts: `packages/contracts/src/pages/.ts`(interface/typeのみ) +3. ui: `packages/ui/src/pages//Page.tsx`(public UI) +4. app page: `app//page.tsx`(Contextからdeps取得してUIへ渡すだけ) + +## deps(依存束)の設計(Upstream内で完結) + +* `AppContext` は `deps` を型付きで提供する +* `createClientDeps` は “public完成実装”として、各ページ用の DataSource を返す(モックOK) + +## 品質ゲート + +* plan に lint/typecheck/build/test/security の実行計画が明記されている +* plan に `app//page.tsx` の依存制約を検証できる受け入れ条件が明記されている +* plan に `contracts` に実装コードが入らないことを検証できる受け入れ条件が明記されている +* plan に DI が AppProvider に固定されることを検証できる受け入れ条件が明記されている + +## テスト設計(Design Issueで必須記載) + +plan には必ず次を明記する。 + +* 対象: どのページ/コンポーネント/ユースケースをテストするか(一覧) +* 方式: Unit(UI) / Unit(Domain, Usecase) / Integration(Route Handler) / Integration(MSW or nock or undici mock) / E2E(Playwright) のどれで守るか +* ケース: 成功/失敗/空/遅延(最低ライン) + 必要箇所はリトライ +* モック方針: Next依存のモック、APIモック(MSW or nock or undici mock)の置き場、共通ヘルパ +* 実行コマンド: リポジトリルート起点(watch/ci/coverage を含む) +* Storybook: 対象UI部品の Story 作成方針(どの状態を Story 化するか) +* バックエンド統合: Route Handler 直接呼び出し時のテストDBまたはトランザクション方針 +* カバレッジ境界: `packages/` / `src/` / `app/` の対象方針(Server Component の扱いを明記) + +## Done + +* 対象画面で「docs→contracts→ui→page→AppProvider」の設計フローが確定している +* 契約のプロパティが明確である +* ルーティングが明確である diff --git a/.github/ISSUE_TEMPLATE/implement_upstream_task.md b/.github/ISSUE_TEMPLATE/implement_upstream_task.md new file mode 100644 index 0000000..2f2b34f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/implement_upstream_task.md @@ -0,0 +1,129 @@ +# [IMPLEMENT] <目的/画面名> + +## 0. AI Agent 契約(最初に読む) + +* あなたは **AIコーディングAgent**。このIssue本文と「入力(SSOT参照セット)」のみで作業する。 +* **SSOTは plan**(確定planが最優先)。矛盾があれば **planを正** とする。 +* **入力不足/矛盾/実装に必要な情報欠落** がある場合、実装を開始しない。 + + * 代わりに `BLOCKER:` として不足点を列挙し、**DESIGNへ差し戻し**(plan修正依頼)を返す。 +* **plan外の仕様追加/推測補完は禁止**。 + +## 1. 目的 + +* ゴール: plan通りにUpstream(public)実装を完了し、CI品質ゲートをすべて通す。 +* 前提: Next.js(App Router) / Plugin型アーキテクチャ / DIP + + * **Composition Root(AppProvider)で依存解決**する。 + +## 2. 入力(SSOT参照セット)※ここが揃っていないと開始禁止 + +### 2.1 確定plan(固定パス / 最優先) + +* `.github/copilot/plans/--implementation-plan.md` + +### 2.2 DESIGN Issue(仕様の背景・補助) + +* [https://github.com/](https://github.com/)//issues/ + +### 2.3 DESIGN PR(設計差分・合意点) + +* [https://github.com/](https://github.com/)//pull/ + + +### 2.5 画面モック/画像(UIの形状合わせ用・仕様追加は禁止) + +* + +## 3. スコープ / 非ゴール + +* 対象: **Upstream(public)のみ**(private参照・実装・言及は禁止) +* 対象: Next.js **App Router(`app/`)** +* 非ゴール: + + * plan外の機能追加 + * 大規模リファクタ + * アーキ変更(DIP/Composition Rootの変更) + +## 4. 変更許容範囲(plan厳守) + +* planからの逸脱: **禁止** +* planが不足している場合: **実装しない** → `BLOCKER` で差し戻し +* planに「任意/裁量」と明記された箇所のみ、最小差分で判断してよい(判断理由をPR本文へ1〜3行で記録) + +## 5. 成果物マニフェスト(必須 / planから転記) + +> **この表が埋まっていない場合は実装開始禁止**。 +> ここに書かれたものだけを作る(= planを転記)。テンプレ側で成果物を決めない。 + +| layer | action(add/modify/delete) | path(相対) | export/API(型名/関数名) | wiring(どこ→どこ) | tests(追加/更新) | +| --------- | ------------------------- | -------- | ------------------ | ------------- | ------------ | +| docs | | | | | | +| contracts | | | | | | +| ui | | | | | | +| app | | | | | | +| deps | | | | | | +| plugins | | | | | | + +## 6. 受入条件(planから転記 / 不足はBLOCKER) + +> planのAcceptance Criteriaをそのまま列挙(AIが増やさない)。 + +* +* +* + +## 7. ガードレール(禁止事項 / 変更してはいけないもの) + +* DO NOT CHANGE(該当があれば列挙。なければ「なし」): + + * <例: APIの外部公開シグネチャ> + * <例: DBスキーマ> +* 仕様追加禁止(plan外の新要件、推測補完) +* private参照禁止(`@company/*`, `**/private/*` など) + +## 8. アーキ制約(DIP / Plugin / Composition Root) + +* 依存解決(DI)は **`src/providers/AppProvider.tsx` のみ** +* `app/**` は薄く(newしない・I/Oしない・depsはContextから取得してUIへ渡す) +* `packages/contracts/**` は interface/type のみ(実装・fetch・URL等の具体禁止) +* `packages/ui/**` は public UI(外部I/O禁止) +* Pluginsは planで要求される場合のみ追加(不要なら作らない) + +## 9. 必読(規約/ゲート) + +* `.github/copilot-instructions.md` +* `.github/instructions/**/*.instructions.md` +* `.github/copilot/30-coding-standards.md` +* `.github/copilot/50-security.md` +* `.github/copilot/60-ci-quality-gates.md` + +## 10. 実行・品質ゲート(Done直結) + +* format / lint +* typecheck +* test(planが要求する範囲) +* build + +## 11. 作業ログ(AI Agentが残す最小記録) + +> 人間向けではなく、**監査と再現**のための最小ログ。 + +* 参照したSSOT: plan / DESIGN Issue / DESIGN PR / docs +* 実装判断(裁量がある場合のみ): 1〜3行 +* 受入条件の担保証跡: テスト名/コマンド/スクショ(必要なら) + +## 12. Done(必須) + +* 成果物マニフェストの項目がすべて実装済み +* 受入条件がすべて満たされる(可能な限りテストで担保。planに従う) +* CI品質ゲートがすべて緑(format/lint/typecheck/test/security/build) +* ドキュメント更新は最小差分(planに従う) + +## 13. BLOCKER(入力不足時の返却フォーマット) + +> 実装開始前に不足があった場合のみ使用。 + +* BLOCKER: <不足点> +* 必要な追記先: +* 理由(1行): <なぜこれが無いと実装できないか> diff --git a/.github/ISSUE_TEMPLATE/ui_upstream_task.md b/.github/ISSUE_TEMPLATE/ui_upstream_task.md new file mode 100644 index 0000000..648e063 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ui_upstream_task.md @@ -0,0 +1,368 @@ +--- +name: UI整備Issueテンプレート(Upstream) +about: Next.js + Atomic Design + Storybook のUI整備専用テンプレートです +title: "[UI] Upstream: ★ここに画面名★ UI整備" +labels: "ui" +assignees: "" +--- + + + +# [UI] Upstream: ★ここに画面名★ UI整備 + +## 1. ゴール + +* UI整備完了後、次のAgentがページ統合作業のみを行えば完了できる状態にする。 +* Atomic Design 粒度で UI を実装し、Storybook で視覚確認できる状態にする。 +* UI は Pure Component とし、ロジックや外部依存を持たない。 + +### 1.1 In Scope + +* React + Tailwind モックの分解 +* Atomic Design 粒度での再設計 +* コンポーネント実装 +* Storybook 生成 +* Storybook Test Runner 対応 +* CSS競合排除 + +### 1.2 技術スタック + +* Next.js +* React +* TailwindCSS +* MUI(Emotion 前提) + +## 2. SSOT参照 + +* 実装は必ず機能設計書(Markdown)を SSOT とする。 +* 設計Issue本文・設計Issueコメント・設計PR本文・設計PRコメントを参照する。 +* SSOT と差異がある場合は実装しない(DESIGN へ差し戻す)。 +* 参照する機能設計書: `` + +### 2.1 参照ブロック(必須) + +* `.github/copilot/00-index.md` +* `.github/copilot-instructions.md` +* `.github/instructions/**/*.instructions.md` +* `.github/copilot/10-requirements.md` +* `.github/copilot/20-architecture.md` +* `.github/copilot/30-coding-standards.md` +* `.github/copilot/40-testing-strategy.md` +* `.github/copilot/50-security.md` +* `.github/copilot/60-ci-quality-gates.md` +* `.github/copilot/80-templates/*` + +## 3. 設計Issueリンク + +* https://github.com///issues/ +* 設計Issue本文とコメントの内容を参照すること。 + +## 4. 設計PRリンク + +* https://github.com///pull/ +* 設計PR本文とコメントの内容を参照すること。 + +## 5. モック情報 + +* モック参照先: `` +* モックは UI 形状合わせのみに使用し、仕様追加は禁止。 + +## 6. Atomic分解方針 + +### 6.1 分解ルール + +* モックをそのままコンポーネント化しない。 +* 設計書に軽く触れられている粒度を最低単位とする。 +* モックより細かい単位で分解する。 +* Atomic Design 階層(Atoms / Molecules / Organisms / Templates)を明示する(Pages は対象外)。 + +### 6.2 分解結果(必須) + +| Atomic階層 | 対象要素 | コンポーネント候補 | 目的/責務 | +| --- | --- | --- | --- | +| Atoms | `<ボタン/ラベル/アイコンなど最小要素>` | ` / Atom` | `<単一責務を1文で記載>` | +| Molecules | `<入力行/カード行など複合要素>` | `Item` | `` | +| Organisms | `<一覧/ヘッダ/ナビ等のセクション>` | `
Panel` | `<セクション全体の表示責務>` | +| Templates(必要な場合のみ) | `<画面レイアウト全体>` | `LayoutTemplate` | `<領域配置・構造提供の責務>` | + +## 7. コンポーネント一覧 + +* 各コンポーネントは「責務」「Props型」「状態保持の有無」「依存コンポーネント」「再利用可否」「表示専用」を必ず定義する。 +* 状態保持は原則なし(保持する場合は理由を明記する)。 +* 状態を持つもの、イベントハンドラを持つもの、MUIの `styled` または `sx` を利用するものは `'use client'` を必須とする。 + +| Atomic階層 | コンポーネント名 | 責務(1文) | Props型定義 | 状態保持 | 制御方式(Controlled/Uncontrolled) | `'use client'` の要否 | 依存コンポーネント | 再利用可否 | 表示専用 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Atoms | `` | `<何を表示するかを1文で記載>` | `Props` | `なし` | `Uncontrolled` | `必須/不要` | `なし` | `可/否` | `はい` | +| Molecules | `Item` | `<何を組み合わせて表示するか>` | `ItemProps` | `なし/あり(理由必須)` | `Controlled/Uncontrolled` | `必須/不要` | `, ...` | `可/否` | `はい` | +| Organisms | `
Panel` | `<セクション単位の表示責務>` | `
PanelProps` | `なし/あり(理由必須)` | `Controlled/Uncontrolled` | `必須/不要` | `Item, ...` | `可/否` | `はい` | +| Templates | `LayoutTemplate` | `<画面構造を提供する責務>` | `LayoutTemplateProps` | `なし` | `Uncontrolled` | `必須/不要` | `
, , ...` | `可/否` | `はい` | + +### 7.0.1 DashboardHeaderの制御方式(必須) + +* `DashboardHeader` の検索入力は `props.searchQuery` と `props.onSearchChange` を用いた完全なControlledコンポーネントとする。 +* `DashboardHeader` 内で検索入力値のために `useState` を使用することを禁止する。 + +### 7.1 命名衝突回避ルール(必須) + +* Atoms は `` を先頭に付与する(例: `Avatar`, `IconButton`)。 +* 代替として接尾辞 `Atom` 方式を採用してもよい(例: `AvatarAtom`)。Issue内で方式を統一する。 +* MUIの同名コンポーネント(`Avatar`, `IconButton`, `Button` 等)を直接exportしない。 +* Barrel export時にMUI名へ再エイリアスしない(例: `export { Avatar as Avatar }` を禁止)。 + +### 7.1.1 アイコン描画ルール(必須) + +* `iconKey` は共通UIコンポーネント(例: ``)に委譲して描画する。 +* `Icon` 内部で `iconKey` と実アイコンの対応表を持つ実装を禁止する。 +* アイコン実体の解決は、AppProvider 等の Context から注入された関数(例: `useIcon(iconKey)`)を使用する。 +* アイコンを各コンポーネントで個別に `@mui/icons-material` から静的importしない。 + +### 7.2 ViewModel/Props型定義(必須) + +* このセクションだけで実装可能なように、コンポーネント用の型を完全展開する。 +* 「設計書参照のみ」「既存型を参照」等の省略記載を禁止する。 +* 型定義には最低限 `ViewModel(参考情報)`、各セクションItem型、各コンポーネントProps型を含める。 +* 特定画面固有の型名や構造をテンプレートに固定しない。画面ごとに必要な型をこのセクションへ展開する。 +* 本Issueの成果物は UI/UXコンポーネント群であり、`PageProps` の実装は対象外とする。 +* ページ系の型(例: `PageProps`, `ViewModel`)はモックデータ作成やStory組み立ての参考情報としてのみ定義してよい。 +* `React.ReactNode` などの型を使用する場合、import前提を明記する(例: `import type { ReactNode } from "react";`)。 + +```ts +// import前提(必須) +import type { ReactNode } from "react"; + +// 例: 画面ViewModel(参考情報) +export interface ViewModel { + header: HeaderView; + sidebar: SidebarView; + // TODO: 画面で使用する一覧/統計/設定などを省略せず記載 +} + +// 例: Item型(必須) +export interface ProjectItem { + id: string; + name: string; + status: string; + // TODO: 必要プロパティを完全展開 +} + +// 例: Organism Props型(必須) +export interface HeaderProps { + title: string; + isLoading?: boolean; + error?: { code: string; message: string }; +} + +export interface ListPanelProps { + items: PrimaryItem[]; + isLoading?: boolean; + error?: { code: string; message: string }; +} + +// 例: 画面固有Item型(必要件数だけ定義) +export interface PrimaryItem { + id: string; + name: string; + status: string; + // TODO: 画面固有フィールドを完全展開 +} + +export interface SecondaryItem { + id: string; + label: string; + value: string; + // TODO: 画面固有フィールドを完全展開 +} + +// 例: Template Props型(必要な場合のみ) +export interface LayoutTemplateProps { + header: ReactNode; + sidebar?: ReactNode; + main: ReactNode; +} +``` + +| 型カテゴリ | 型名 | 定義場所(このIssue本文内) | 完全展開 | +| --- | --- | --- | --- | +| ViewModel(参考情報) | `ViewModel` | `7.2` | `任意` | +| Item DTO | `PrimaryItem / SecondaryItem` | `7.2` | `必須` | +| Component Props | `Props(Atoms/Molecules/Organisms/Templates)` | `7.2` | `必須` | + +## 8. CSS責務定義 + +### 8.1 CSS前提 + +* StyledEngineProvider の `injectFirst` を前提とする。 +* MUI は構造/コンポーネント責務、Tailwind はユーティリティ用途のみ。 +* 同一要素で同一CSSプロパティの多重指定を禁止する。 +* MUI と Tailwind で同一スタイル責務を持たせない。 +* UpstreamではPC画面専用。メディアクエリ/レスポンシブ設計/スマートフォン対応を禁止する。 +* 将来拡張のためだけの過剰抽象化(未使用の汎用レイヤ、不要なジェネリクス、過度なDI)は禁止する。 +* ただしAtomic再利用に必要な最小抽象化(size variants、tone variants、共通Props化)は許可する。 + +### 8.2 CSS責務一覧(必須) + +| 対象 | MUI責務 | Tailwind責務 | 競合回避方針 | +| --- | --- | --- | --- | +| `` | `<構造/寸法/色/境界線など>` | `<余白/整列/表示制御など>` | `<同一CSSプロパティを片側固定>` | + +### 8.3 Atoms単位スタイル責務(推奨) + +* Atomsごとに「MUI利用/Tailwind利用/禁止プロパティ」を1行で固定する。 +* 競合しやすい `padding`, `margin`, `font-size`, `color`, `border`, `background`, `width`, `height` は必ず担当を固定する。 +* ※ これらは「スタイルを指定する場合の管轄」であり、デザイン上不要なプロパティ(`padding: 0` 等)の明示的な初期化を強制するものではない。 + +| Atom名 | MUI(sx/styled)利用 | Tailwind class利用 | 担当固定プロパティ(必須) | 禁止プロパティ(禁止側に記載) | +| --- | --- | --- | --- | --- | +| `` | `あり/なし` | `あり/なし` | `padding:, margin:, font-size:, color:, border:, background:, width:, height:` | `例: で指定禁止` | +| `Atom` | `あり/なし` | `あり/なし` | `padding:, margin:, font-size:, color:, border:, background:, width:, height:` | `例: で指定禁止` | + +### 8.4 テーマ変数変換ルール(必須) + +* モック由来のTailwind色クラスをハードコード値へ置き換えない。 +* 色・背景・枠線は MUI Theme 変数へ変換して指定する(例: `theme.palette.*`)。 + +| モック表現(Tailwind例) | MUI Theme変換先(例) | 禁止事項 | +| --- | --- | --- | +| `text-gray-500` | `theme.palette.text.secondary` | `color: \"#6b7280\"` の直接指定禁止 | +| `text-gray-900` | `theme.palette.text.primary` | `color` の16進ハードコード禁止 | +| `bg-white` | `theme.palette.background.paper` | 背景色の固定値指定禁止 | +| `bg-gray-50` | `theme.palette.background.default` | 背景色の固定値指定禁止 | +| `border-gray-200` | `theme.palette.divider` | 枠線色の固定値指定禁止 | +| `text-blue-600` | `theme.palette.primary.main` | 主要色の固定値指定禁止 | +| `text-green-600` | `theme.palette.success.main` | 状態色の固定値指定禁止 | +| `text-amber-600` | `theme.palette.warning.main` | 状態色の固定値指定禁止 | + +### 8.5 バリアント/デザイン・トークン対応表(必須) + +* `size` / `tone` は下表の対応を必須とする。 +* 下表にない値は、Issue本文に追加して合意されるまで実装しない。 + +| 種別 | 値 | マッピング先(必須) | +| --- | --- | --- | +| `size` | `sm` | `16px` | +| `size` | `md` | `24px` | +| `size` | `lg` | `32px` | +| `tone` | `primary` | `theme.palette.primary.main` | +| `tone` | `success` | `theme.palette.success.main` | +| `tone` | `warning` | `theme.palette.warning.main` | +| `tone` | `error` | `theme.palette.error.main` | +| `tone` | `neutral` | `theme.palette.text.secondary` | + +### 8.6 Typographyマッピング(必須) + +* 見出し・本文・数値は MUI `Typography` の `variant` と `component` を固定する。 +* 見出し用途を `div` / `span` で代替する実装を禁止する。 + +| 対象コンポーネント | 用途 | MUI `Typography` variant | HTMLタグ(component) | +| --- | --- | --- | --- | +| `LcSectionTitle` | セクション見出し | `h6` | `h2` | +| `LcSectionTitle` | 補助説明 | `body2` | `p` | +| `LcMetricValue` | 主要数値 | `h5` | `p` | +| `LcMetricValue` | 単位/補足 | `caption` | `span` | +| `LcStatusChip` | 状態ラベル | `caption` | `span` | + +## 9. Storybook生成要件 + +* 全てのAtomic Design階層(Atoms / Molecules / Organisms / Templates)でStoryを作成する。 +* Propsバリエーションがある場合は全パターン作成する。 +* Controls有効化、Docs自動生成を有効化する。 +* Storyのデコレーターで、UI描画に必要なダミーContext(例: アイコン解決用 `IconResolverContext`)を注入する。 +* Storybook Test Runnerで以下を満たすこと。 + * 全Storyのレンダリング成功 + * console error なし + * interaction test成功(存在する場合) + * a11y違反なし(導入済みの場合) + +### 9.1 必須Story一覧(推奨) + +* 生成物のSSOTとして、コンポーネントごとに最低限必要なStoryキーを列挙する。 +* `default` のみで完了扱いにしない。状態/variantがある場合は対応Storyを必須化する。 + +| Atomic階層 | コンポーネント名 | 必須Storyキー | Play関数での検証内容 | +| --- | --- | --- | --- | +| Atoms | `` | `default`, `disabled` | `disabled` で `toBeDisabled()` を検証 | +| Molecules | `Item` | `default`, `empty` | `empty` で空状態文言/件数0表示を検証 | +| Organisms | `
Panel` | `default`, `loading`, `error` | `loading` でローディング表示、`error` でエラー文言表示を検証 | +| Templates | `LayoutTemplate` | `default` | 主要領域(header/main等)の表示を検証 | +| Molecules | `SettingActionButton` | `default`, `disabled` | `disabled` でクリック不能かつ `onClick` スパイ未呼出しを検証 | + +### 9.2 StoryキーとProps契約の整合ルール(必須) + +* `loading` story を要求するコンポーネントは、対応するPropsに `isLoading?: boolean` を必ず定義する。 +* `error` story を要求するコンポーネントは、対応するPropsに `error?: { code: string; message: string }` を必ず定義する。 +* `empty` story を要求するコンポーネントは、対応するPropsに空配列を受け取る一覧型(例: `items: Item[]`)を必ず定義する。 +* `disabled` story を要求するコンポーネントは、対応するPropsに `disabled?: boolean` を必ず定義する。 + +| Storyキー | Props必須契約 | 例 | +| --- | --- | --- | +| `loading` | `isLoading?: boolean` | `ProjectListPanelProps` | +| `error` | `error?: { code: string; message: string }` | `BudgetExecutionPanelProps` | +| `empty` | `items: Item[]`(空配列許容) | `MemberListPanelProps` | +| `disabled` | `disabled?: boolean` | `LcIconButtonProps` | + +### 9.3 Mock Provider注入要件(必須) + +* Storybook では `preview.ts` または各Storyの `decorators` で Mock Provider をラップする。 +* アイコン描画は、`useIcon(iconKey)` を返すダミー実装を注入して検証する。 +* Context依存の値をStory内で直接ハードコードせず、Provider経由で注入する。 + +## 10. 変更禁止範囲 + +### 10.1 Out of Scope(実装禁止) + +* API呼び出し / データフェッチ +* API import / fetch / axios / useQuery 系 +* 状態管理ロジック / useEffect / 非同期処理 +* グローバル状態参照(Zustand/Recoil/Redux 等) +* ルーティング処理 / ページ統合 / コンテナ実装 +* サーバーコンポーネント利用 + +### 10.2 変更禁止パス・モジュール(必要に応じて追記) + +* DO NOT CHANGE: + * なし + +## 11. 品質ゲート + +* format +* lint +* typecheck +* unit test +* security +* Storybook build +* Storybook Test Runner + +## 12. Done定義 + +* 6章で定義したAtomic分解に対応するUIコンポーネントが実装されている +* 7章で定義したProps型と実装コードが一致している(型エラーなし) +* 7章で定義した `'use client'` 要否に違反がない +* `DashboardHeader` がControlled実装(`searchQuery`/`onSearchChange`)であり、内部 `useState` を持たない +* 命名衝突回避ルールに違反するコンポーネント名が存在しない +* MUI同名コンポーネントの直接exportが存在しない +* アイコン描画が共通 `` 経路へ統一され、解決がContext注入関数(例: `useIcon`)経由になっている +* UI実装にAPI呼び出し・非同期処理・グローバル状態参照が混入していない +* CSS責務定義(8章)どおりに実装され、同一プロパティの多重指定がない +* 色指定が8.4のテーマ変数変換ルールに従い、ハードコード色を使用していない +* 8.5のバリアント/デザイン・トークン対応表に従って `size` / `tone` が実装されている +* 8.6のTypographyマッピングに従って `variant` と `component` が設定されている +* 9.1で定義した必須Storyキーが全コンポーネントで作成されている +* 9.1で定義したPlay関数アサーションが各Storyに実装されている +* 9.2で定義したStoryキーとProps契約の整合ルールを満たしている +* 9.3のMock Provider注入要件を満たし、StorybookでContext依存が解決されている +* Storybook上で全Storyが表示確認でき、console error が発生しない +* Storybook Test Runner が成功する +* format / lint / typecheck / unit test / security / Storybook build が成功する +* コンポーネントは `pages` 配下に存在しない +* データ未接続(Container未実装・外部依存なし)を維持している diff --git a/.github/PULL_REQUEST_TEMPLATE/blind.md b/.github/PULL_REQUEST_TEMPLATE/blind.md index 681fab2..ea02ee6 100644 --- a/.github/PULL_REQUEST_TEMPLATE/blind.md +++ b/.github/PULL_REQUEST_TEMPLATE/blind.md @@ -196,9 +196,11 @@ AIが生成したコード(生成物)の利用方法を下記のテーブル * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> -## 既存ログ(前回まで) - -```text -<> -``` diff --git a/.github/PULL_REQUEST_TEMPLATE/design.md b/.github/PULL_REQUEST_TEMPLATE/design.md index a974203..5db0def 100644 --- a/.github/PULL_REQUEST_TEMPLATE/design.md +++ b/.github/PULL_REQUEST_TEMPLATE/design.md @@ -297,9 +297,7 @@ codingAgentが「手を動かしたい」欲をここに寄せます。**サン * フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> * フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> - -## 既存ログ(前回まで) - -```text -<> -``` +* フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(見出し/ADR/Commit):<> 状態(対応済/保留/却下):<> diff --git a/.github/PULL_REQUEST_TEMPLATE/implement.md b/.github/PULL_REQUEST_TEMPLATE/implement.md index 944c3af..16829eb 100644 --- a/.github/PULL_REQUEST_TEMPLATE/implement.md +++ b/.github/PULL_REQUEST_TEMPLATE/implement.md @@ -195,9 +195,9 @@ diffの目次(ファイル×変更概要)を下記のテーブルに記載 * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> * フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> - -## 既存ログ(前回まで) - -```text -<> -``` +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> +* フィードバック(要約):<> 対応(何を変えた):<> 対象(ファイル/行/Commit):<> 状態(対応済/保留/却下):<> diff --git a/.github/PULL_REQUEST_TEMPLATE/research.md b/.github/PULL_REQUEST_TEMPLATE/research.md index b05aa51..53d9167 100644 --- a/.github/PULL_REQUEST_TEMPLATE/research.md +++ b/.github/PULL_REQUEST_TEMPLATE/research.md @@ -257,9 +257,9 @@ diffの目次(ファイル×変更概要)を下記のテーブルに記載 ## 追記(今回分) * フィードバック(要約):<> 対応:<> 対象:<> 状態:<> - -## 既存ログ(前回まで) - -```text -<> -``` +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> +* フィードバック(要約):<> 対応:<> 対象:<> 状態:<> diff --git a/.github/copilot/00-index.md b/.github/copilot/00-index.md index e9ebc82..6ed44c8 100644 --- a/.github/copilot/00-index.md +++ b/.github/copilot/00-index.md @@ -26,6 +26,11 @@ - IMPLEMENT PR → [.github/PULL_REQUEST_TEMPLATE/implement.md](../PULL_REQUEST_TEMPLATE/implement.md) - RESEARCH PR → [.github/PULL_REQUEST_TEMPLATE/research.md](../PULL_REQUEST_TEMPLATE/research.md) - BLIND PR → [.github/PULL_REQUEST_TEMPLATE/blind.md](../PULL_REQUEST_TEMPLATE/blind.md) +- テンプレートの役割を混在させないこと: + - PRテンプレート(`.github/PULL_REQUEST_TEMPLATE/*.md`)は人間レビュー用の説明フォーマットとして使う。 + - `80-templates/implementation-plan.md` は設計Agentから製造Agentへの引き渡し仕様として使う。 + - IMPLEMENT Issue の製造時は、確定した plan Markdown を一次入力(主入力)として実装する。 +- DIP固定ルールはテンプレートではなく SSOT を正とする。詳細は `copilot-instructions.md` と `20-architecture.md` を参照する。 - **設計フェーズ (Phase A)**: `80-templates/implementation-plan.md` に沿って plan を作成し、`10-60` の仕様を満たすこと。 - **実装フェーズ (Phase B)**: 確定 plan の範囲内で実装し、`60-ci-quality-gates.md` の品質ゲートを通過させること。 - 仕様変更・追記は本ディレクトリに集約し、重複や分散を避ける。 diff --git a/.github/copilot/05-structure/nextjsrepo.md b/.github/copilot/05-structure/nextjsrepo.md new file mode 100644 index 0000000..100b088 --- /dev/null +++ b/.github/copilot/05-structure/nextjsrepo.md @@ -0,0 +1,190 @@ +# Next.js Repo 構成定義 — Upstream Demo SSOT + +> NOTE: 本書は Next.js Repo 構成定義レイヤの SSOT であり、`.github/ISSUE_TEMPLATE/design_upstream_task.md` は本書と整合するように保守します。 +> RULE / DO NOT / EXAMPLE / NOTE のタグを使って規範を明示します。 + +--- + +## 1. 目的と適用範囲 +- RULE: 対象は Upstream(public) の Next.js リポジトリ構成であり、設計/実装時のディレクトリ責務の単一情報源とする。 +- RULE: 本書は `.github/copilot/00-index.md` から最優先で参照される構成定義レイヤとする。 + +--- + +## 2. 基本原則 +- RULE: Next.js アプリはリポジトリルート直下に配置する(`apps/` 配下に置かない)。 +- RULE: ルーティングは App Router(`app/`)を採用する。 +- RULE: 依存解決(DI)は `src/providers/AppProvider.tsx` のみで行う。 +- RULE: `packages/contracts` は interface/type のみを持ち、実装を含めない。 +- RULE: `packages/ui` は public UI を保持し、会社固有前提を入れない。 +- RULE: 実装の差し替え点は `packages/plugins` に集約する。 +- DO NOT: `app/*/page.tsx` で具象実装(HTTP/DB/Storage)を直接 `new` しない。 +- DO NOT: `contracts` に URL / 認証 / fetch などの実装詳細を持ち込まない。 + +--- + +## 3. 標準ディレクトリ構成(固定) +```text +repo-root/ +├── app/ +│ ├── layout.tsx +│ ├── page.tsx +│ └── / +│ ├── page.tsx +│ ├── layout.tsx +│ ├── providers.tsx +│ └── _components/ +│ +├── public/ +│ +├── src/ +│ ├── providers/ +│ │ ├── AppProvider.tsx +│ │ └── AppContext.tsx +│ ├── composition/ +│ │ ├── createClientContainer.ts +│ │ ├── createServerContainer.ts +│ │ ├── registerPlugins.ts +│ │ └── pluginRegistry.ts +│ └── lib/ +│ ├── createClientDeps.ts +│ └── createServerDeps.ts +│ +└── packages/ + ├── contracts/ + │ └── src/ + │ ├── index.ts + │ ├── domain/ + │ ├── ports/ + │ ├── usecases/ + │ ├── dto/ + │ └── pages/ + │ └── .ts + │ + ├── ui/ + │ └── src/ + │ ├── pages/ + │ │ └── Page/ + │ │ └── Page.tsx + │ ├── atoms/ + │ ├── molecules/ + │ ├── organisms/ + │ └── templates/ + │ + └── plugins/ + └── src/ + ├── index.ts + ├── http/ + ├── storage/ + ├── telemetry/ + └── auth/ +``` + +--- + +## 4. 参照境界(依存方向) +- RULE: `app/*/page.tsx` は `packages/contracts/*` / `packages/ui/*` / `AppContext` のみ参照可能とする。 +- RULE: `packages/ui` は props を受けて描画し、usecase 実行や I/O 実装を持たない。 +- RULE: usecase 呼び出しは `app/` または `src/*`(container 側)で行う。 +- RULE: `app/**/providers.tsx` は画面スコープ Provider とし、依存解決は行わない。 +- RULE: `src/composition` は依存生成、`src/providers` は注入(Provider)を担当する。 + +--- + +## 5. packages & TypeScript paths +- RULE: import path の単一情報源は `tsconfig.json` の `compilerOptions.paths` とする。 +- RULE: `packages/contracts` / `packages/ui` / `packages/plugins` はパッケージ境界を維持し、エイリアス経由で参照する。 +- RULE: 推奨エイリアスは以下を使用する。 + - `@contracts/*` -> `packages/contracts/src/*` + - `@ui/*` -> `packages/ui/src/*` + - `@plugins/*` -> `packages/plugins/src/*` + - `@app/*` -> `src/*` +- DO NOT: `../` で `app` と `packages` を横断する相対 import を行わない。 +- DO NOT: `app/*/page.tsx` から `packages/contracts/*` / `packages/ui/*` / `AppContext` 以外を直接参照しない。 +- EXAMPLE: `tsconfig.json` の最小例 +```json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@contracts/*": ["packages/contracts/src/*"], + "@ui/*": ["packages/ui/src/*"], + "@plugins/*": ["packages/plugins/src/*"], + "@app/*": ["src/*"] + } + } +} +``` + +--- + +## 6. CSS 方針 +- RULE: MUI は Emotion エンジンを前提に運用する。 +- RULE: `styled` は `@emotion/styled` を標準とする。 +- RULE: Tailwind はユーティリティ用途(レイアウト/微調整/状態/レスポンシブ)に限定する。 +- RULE: `StyledEngineProvider injectFirst` を使用し、MUI の注入順を固定する。 +- DO NOT: 同一要素で同一CSSプロパティを多重指定しない。 +- DO NOT: MUI を `@mui/styled-engine-sc` へ切り替えない。 + +--- + +## 7. 設計Issue運用 +- RULE: DESIGN Issue の成果物は `.github/copilot/plans/-page-.md` のみとする。 +- RULE: DESIGN Issue ではコード修正を禁止し、設計を製造Agentへ引き継ぐことを目的とする。 +- RULE: 実装は IMPLEMENT Issue で実施し、plan に定義された受け入れ条件で検証する。 + +--- + +## 8. テスト戦略(Frontend + Backend) +- RULE: Storybook を UI コンポーネントの確認基盤として採用し、コンポーネント単位の状態を Story として管理する。 +- RULE: UIユニットテストは Vitest + React Testing Library を標準とし、DOM環境は jsdom(または happy-dom)を採用する。 +- RULE: API通信を伴うUIのテストは MSW によりHTTPをモックし、成功/失敗/遅延/空を最低1ケースずつ含める。 +- RULE: ユースケース/ドメイン(`packages/**` の純TSロジック)は Vitest のユニットテストで検証する。 +- RULE: HTTP境界(Route Handlers)は「ハンドラを直接呼ぶ統合テスト」を実施し、テストDBまたはトランザクションで検証する。 +- RULE: 外部API連携は `nock` / `undici mock` / MSW(node)のいずれかでモックし、失敗/遅延/リトライを含めて検証する。 +- RULE: E2Eは Playwright を採用し、主要導線のスモークテストを最小セットで維持する。 +- RULE: E2E(Playwright)は「UI + Route + DB」を通すスモークで最終保証を行う。 +- RULE: Next.js依存(`next/navigation`, `next/image`, `next/link`)はテスト用モック/スタブを共通化し、各テストに重複実装しない。 +- RULE: カバレッジは `packages/` と `src/` を中心に測定し、`app/` は「テスト対象の境界」を明記する。 +- RULE: DESIGN Issue では「テスト実行計画」を plan に記載し、IMPLEMENT Issue で実行結果を検証する。 +- EXAMPLE: テスト探索設定(Vitest) +```ts +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: [ + "app/**/?(*.)+(test).[tj]sx?", + "src/**/?(*.)+(test).[tj]sx?", + "packages/**/?(*.)+(test).[tj]s?" + ], + environment: "jsdom" + } +}); +``` +- EXAMPLE: 実行コマンド +```bash +npm run lint +npm run typecheck +npm run test +npm run build +npm run storybook +``` + +--- + +## 9. 認証情報 / credentials 運用 +- RULE: Secrets はリポジトリへコミットしない。 +- RULE: `.env` には秘密値を直接記載せず、必要時は参照情報(キー名/パス名)のみを記載する。 +- RULE: 認証情報の実体は GitHub Secrets / 環境変数注入 / 外部シークレットストアで管理する。 +- RULE: public upstream のため、plan・ログ・PR本文に秘密情報や復元可能な値を出力しない。 +- DO NOT: テストデータとして実在のトークン/鍵/個人情報を使用しない。 + +--- + +## 10. Dependabot +- RULE: `.github/dependabot.yml` はリポジトリルートで一元管理する。 +- RULE: `open-pull-requests-limit` と `groups` を設定し、依存更新PRの同時発生を制御する。 +- RULE: 更新対象ディレクトリは実在パスに限定する(例: `/`、必要時は `/mock/v1/web`)。 +- RULE: 週次更新を基本とし、security updates は優先レビューする。 +- DO NOT: 依存ファイルが存在しないディレクトリを `directory` に指定しない。 diff --git a/.github/copilot/05-structure/sakura_repo.backup.md b/.github/copilot/05-structure/sakura_repo.backup.md new file mode 100644 index 0000000..dcf15d2 --- /dev/null +++ b/.github/copilot/05-structure/sakura_repo.backup.md @@ -0,0 +1,56 @@ +# sakura_repo.md — レンタルサーバー専用リポジトリ構成定義 + +## 概要 +- レンタルサーバー環境で単一アプリを運用するための構成定義レイヤー。 +- リポジトリルートはサーバー上のユーザープロファイルパス(例: `/home//`)と一致し、モノレポ構成とは独立して扱う。 +- CI は GitHub Actions ではなく、専用ランナーがリポジトリ全体をクローンしてミラーリングする前提。 + +## リポジトリ前提 +- 配置パス: `/home//` 直下にリポジトリを配置する。 +- 単一アプリ構成を前提とし、外部リポジトリへのシンボリックリンクやサブモジュール依存は行わない。 +- composer による依存管理を行い、PHP プロジェクト運用に限定する。 + +## 運用ポリシー +- テストは PHPUnit を使用し、バージョンを 10.5.38 に固定する。 +- 専用ランナーが 1 時間ごとにポーリングし、GitHub 上の当該リポジトリをクローンしてレンタルサーバー上に完全同期(ミラーリング)する。 + - 差分コピーではなく全体同期を原則とする。 + - デプロイおよび SSH 転送ログはランナー側で保存し、サーバーには Secrets を残さない。 +- ブランチ運用は単一アプリ向けの簡潔さを優先し、モノレポと異なる開発・検証サイクルを維持する。 + +## CI・テスト実行 +ローカルまたは専用ランナー上で以下を実行する。 + +```bash +$ vendor/bin/phpunit --configuration phpunit.xml +# 使用バージョン: phpunit/phpunit 10.5.38 +``` + +## デプロイ/ポーリング手順 +1. 専用ランナーが 1 時間ごとにリポジトリをクローン。 +2. 最新コミットで PHPUnit 10.5.38 を実行し、失敗した場合はデプロイを中断。 +3. 成功時にリポジトリ全体を `/home//` 直下へミラーリングして反映。 +4. 同期後のログをサーバー側に保管し、次回ポーリングで再評価する。 + +## 拡張方針 +- 将来的にモノレポ定義と統合する場合でも、本ドキュメントの内容を構成層として吸収できるよう章立てを維持する。 +- CI/デプロイの追加要件が発生した際は、専用ランナー前提を崩さずに拡張する。 + +## 品質ゲート +- PHPUnit 10.5.38 が成功すること。 +- デプロイはミラーリング(完全同期)で行われ、Secrets をサーバー上に残さないこと。 +- Markdown 構文エラーがないこと。 + +## 参照ポリシー +- 本ドキュメントはレンタルサーバー向けに単独完結させ、他の構成定義ファイルへの参照を前提としない。 + +## フロー可視化 +```mermaid +flowchart TD + A[開発者] --> B[sakura_repo.md 作成・更新] + B --> C[PHPUnit 10.5.38 ルール適用] + C --> D[ローカルまたは専用ランナーでテスト実行] + D --> E[テスト結果確認] + E --> F[専用ランナーがリポジトリを完全同期デプロイ] + F --> G[1時間ごとのポーリングで再実行] + G --> H[構成定義を見直し・反映] +``` diff --git a/.github/copilot/20-architecture.md b/.github/copilot/20-architecture.md index bd5b606..65cb6d9 100644 --- a/.github/copilot/20-architecture.md +++ b/.github/copilot/20-architecture.md @@ -23,6 +23,13 @@ flowchart TD - 仕様の更新・判断根拠: `.github/copilot/` と `70-adr/` - 実務上の禁止事項・コマンド: `.github/instructions/`(パス適用) - 実装・テスト計画: `80-templates/implementation-plan.md` +- レビュー本文(人間向け): `.github/PULL_REQUEST_TEMPLATE/*.md` + + +## テンプレートの役割分離 +- PRテンプレートは「人間レビューのための説明」を記録する。 +- `80-templates/implementation-plan.md` は「設計Agentから製造Agentへの実装仕様引き渡し」を記録する。 +- IMPLEMENT フェーズでは、確定 plan Markdown を一次入力として実装し、プロンプトに仕様全文の再記載を要求しない。 ## 依存と適用範囲 - すべてのタスクは `00-index.md` の参照順を守る。 @@ -31,6 +38,8 @@ flowchart TD ## Issue 運用ルール - Copilot を Issue にアサインする前に、必要な要件は本文へすべて記載する(アサイン後のコメントは認識されないため、追記は PR コメントで渡す)。 - 設計と実装の Issue は分離し、設計 Issue はドキュメントのみの PR、実装 Issue は確定 plan のパスを明示してその範囲に限定する。 +- 設計 Issue の plan では、設計行為そのものをゴール/要件にしない。実装で変化する機能/画面/API/データ契約のみを対象化する。 +- 設計 Issue の要件は、実装後に観測できる受入条件(テスト可能)で固定し、実装 Issue ではその plan Markdown を一次入力として実装する。 - 不確実性解消のために **[RESEARCH]** Issue を設け、コード変更禁止を基本とする(成果物は docs/research や Issue/PR コメントに集約し、結論は ADR/Requirements へ昇格させてから Design に渡す)。 - 仕様参照を避けたい場合は、任意フェーズの Issue に付与できるモディファイアとして **[BLIND]** を用いる。本文に必要情報を埋め込み、参照禁止範囲と変更可否を明示する。成果物は最小差分かつ本文完結とする。 - 推奨する最小セット: `[RESEARCH]`(調査のみ)、`[DESIGN]`(plan確定)、`[IMPLEMENT]`(plan通り実装)、`[BLIND]`(任意フェーズに付与可能な仕様参照抑制モディファイア)。 diff --git a/.github/copilot/30-coding-standards.md b/.github/copilot/30-coding-standards.md index b23bd90..f52d141 100644 --- a/.github/copilot/30-coding-standards.md +++ b/.github/copilot/30-coding-standards.md @@ -4,6 +4,7 @@ - 互換性維持をデフォルトとし、破壊的変更は移行策・理由を plan / ADR に記載する。 - Python: 型ヒント必須、例外は無視せずログを記録して再送出し、`print` ではなく構造化ログを使う。 - 依存追加は最小限とし、バージョンをピン止めして `requirements.txt` / `constraints` に反映する。 +- 依存追加は最小限とし、バージョンをピン止めして `package.json` / `package-lock.json` に反映する。 - ログ/コメント/Doc は簡潔に。秘密情報・個人情報をログやコメントに残さない。 - テスト可能な構造(副作用を分離、関数・メソッドを小さく)を心掛ける。 - コミットメッセージ: diff --git a/.github/copilot/80-templates/implementation-plan.md b/.github/copilot/80-templates/implementation-plan.md index 6789cab..9235f18 100644 --- a/.github/copilot/80-templates/implementation-plan.md +++ b/.github/copilot/80-templates/implementation-plan.md @@ -1,32 +1,1128 @@ # Implementation Plan テンプレート -## 1. 機能要件 / 非機能要件 -- 機能要件: -- 非機能要件: - -## 2. スコープと変更対象 -- 変更ファイル(新規/修正/削除): -- 影響範囲・互換性リスク: -- 外部依存・Secrets の扱い: - -## 3. 設計方針 -- 責務分離 / データフロー(必要なら Mermaid 1 枚): -- エッジケース / 例外系 / リトライ方針: -- ログと観測性(漏洩防止を含む): - -## 4. テスト戦略 -- テスト観点(正常 / 例外 / 境界 / 回帰): -- モック / フィクスチャ方針: -- テスト追加の実行コマンド(例: `python -m pytest`): - -## 5. CI 品質ゲート -- 実行コマンド(format / lint / typecheck / test / security): -- 通過基準と失敗時の対応: - -## 6. ロールアウト・運用 -- ロールバック方法: -- 監視・運用上の注意: - -## 7. オープンな課題 / ADR 要否 -- 未確定事項: -- ADR に残すべき判断: +--- + +## 0. 実装入力コンテキスト + +| 項目 | 記入 | +| --- | --- | +| 対象Issue | <> | +| 対象リポジトリ内パス(実装起点) | <> | + +運用補足: Agentが実装時に直接参照する入力のみを記載する。未確定は `TBD(理由/決定条件/期限)` で記載する。 + +### 0.1 変更サマリ一覧(複数行) + +| 区分(追加/修正/削除) | 対象(機能/画面/API) | 変更概要 | +| --- | --- | --- | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 追加 | <> | <> | +| 修正 | <> | <> | +| 修正 | <> | <> | +| 修正 | <> | <> | +| 修正 | <> | <> | +| 修正 | <> | <> | +| 修正 | <> | <> | +| 削除 | <> | <> | +| 削除 | <> | <> | +| 削除 | <> | <> | +| 削除 | <> | <> | +| 削除 | <> | <> | +| 削除 | <> | <> | + +運用補足: 行数が不足する場合は同じ形式で行を追加する。 + +### 0.2 入力制約一覧(複数行) + +| 制約区分(互換性/禁止事項/期限/その他) | 制約内容 | 適用対象 | +| --- | --- | --- | +| 互換性 | <> | <> | +| 互換性 | <> | <> | +| 互換性 | <> | <> | +| 互換性 | <> | <> | +| 互換性 | <> | <> | +| 禁止事項 | <> | <> | +| 禁止事項 | <> | <> | +| 禁止事項 | <> | <> | +| 禁止事項 | <> | <> | +| 禁止事項 | <> | <> | +| 期限 | <> | <> | +| 期限 | <> | <> | +| 期限 | <> | <> | +| 期限 | <> | <> | +| 期限 | <> | <> | +| その他 | <> | <> | +| その他 | <> | <> | + +運用補足: 行数が不足する場合は同じ形式で行を追加する。 + +### 0.3 関連機能・関連仕様一覧(複数行) + +| 種別(要件/設計方針/ADR/調査/既存実装/外部仕様/その他) | パス/識別子 | この設計での利用目的 | +| --- | --- | --- | +| 要件 | <> | <> | +| 要件 | <> | <> | +| 要件 | <> | <> | +| 要件 | <> | <> | +| 設計方針 | <> | <> | +| 設計方針 | <> | <> | +| 設計方針 | <> | <> | +| ADR | <> | <> | +| ADR | <> | <> | +| ADR | <> | <> | +| 調査 | <> | <> | +| 調査 | <> | <> | +| 既存実装 | <> | <> | +| 既存実装 | <> | <> | +| 外部仕様 | <> | <> | +| 外部仕様 | <> | <> | +| その他 | <> | <> | +| その他 | <> | <> | + +運用補足: 行数が不足する場合は同じ形式で行を追加する。 + +--- + +## 1. 実装対象機能と機能ゴール + +| 項目 | 内容 | 根拠 | +| --- | --- | --- | +| 実装対象詳細(機能/画面/API) | <> | <> | +| 機能ゴール(実装後に観測できるユーザーユース) | <> | <> | +| 非ゴール(今回やらないこと) | <> | <> | +| 完了条件(実装完了の判定) | <> | <> | +| 受入確認手順(1行で再現可能) | <> | <> | + +運用補足: 「完了条件」はテストまたは確認手順で判定可能な文で記載する。 +運用補足: 「受入確認手順」はコマンド/操作を1行で再現できる形で記載する(例: `/dashboard` 表示確認 + lint 実行)。 + +--- + +## 2. 前提・制約(SSOT) + +| 種別 | 内容 | 根拠(ファイル/ADR/Issue) | +| --- | --- | --- | +| 参照したSSOT | <> | <> | +| Next.js構成前提(app/src/packages) | <> | <> | +| 依存境界前提(page.tsx / AppProvider / contracts) | <> | <> | +| 技術制約(互換性/期限/運用/セキュリティ) | <> | <> | +| 未確定前提(TBD) | TBD(理由/決定条件/期限) | <> | + +運用補足: 根拠は `ファイルパス` または `Issue/ADR` を必ず記載する。 + +--- + +## 3. 要件定義(実装受入条件) + +### 3.1 機能要件 + +| ID | 要件 | 受入条件(テスト可能な形) | +| --- | --- | --- | +| FR-01 | <> | <> | +| FR-02 | <> | <> | +| FR-03 | <> | <> | +| FR-04 | <> | <> | +| FR-05 | <> | <> | +| FR-06 | <> | <> | +| FR-07 | <> | <> | +| FR-08 | <> | <> | +| FR-09 | <> | <> | +| FR-10 | <> | <> | + +運用補足: IDは `FR-01` 形式の連番(欠番禁止)。 + +### 3.2 非機能要件 + +| ID | 要件 | 受入条件(テスト可能な形) | +| --- | --- | --- | +| NFR-01 | <> | <> | +| NFR-02 | <> | <> | +| NFR-03 | <> | <> | +| NFR-04 | <> | <> | +| NFR-05 | <> | <> | +| NFR-06 | <> | <> | +| NFR-07 | <> | <> | + +運用補足: IDは `NFR-01` 形式の連番(欠番禁止)。 + +--- + +## 4. スコープ境界 + +運用補足: この章は「実装時の影響範囲」を記載する。設計Agentの作業内容や設計書ファイル変更そのものは書かない。 +運用補足: この章は「この設計を実装したときの想定差分」を書く。現在のDesign PR差分は書かない。 + +### 4.0 スコープ境界の定義(機能単位) + +| 区分(In-Scope/Out-of-Scope) | 対象機能/責務 | 判定理由 | +| --- | --- | --- | +| In-Scope | <> | <> | +| In-Scope | <> | <> | +| In-Scope | <> | <> | +| In-Scope | <> | <> | +| In-Scope | <> | <> | +| Out-of-Scope | <> | <> | +| Out-of-Scope | <> | <> | +| Out-of-Scope | <> | <> | +| Out-of-Scope | <> | <> | +| Out-of-Scope | <> | <> | + +運用補足: 対象機能/責務は「実装で変更される責務単位」を書く(例: 初期表示データ取得責務、エラー標準化責務、import境界強制責務)。 +運用補足: 判定理由は FR/NFR への対応、または非対象理由(別Issue/条件未達)で書く。 +運用補足: ファイル列挙は `8.1 変更予定ファイル一覧` に一本化する。 + +### 4.2 実装時の影響範囲・互換性リスク + +| 影響対象 | 結論(影響あり/なし/未確定) | 影響内容 | +| --- | --- | --- | +| UI/画面 | <> | <> | +| API契約 | <> | <> | +| データ互換 | <> | <> | +| 外部依存 | <> | <> | +| CI/運用 | <> | <> | + +運用補足: 結論は `影響あり` / `影響なし` / `未確定` のいずれかを記載する。 +運用補足: 結論が `未確定` の場合は、関連セクションに `TBD(理由/決定条件/期限)` を記載する。 +運用補足: 影響内容は「どの挙動/契約/運用がどう変わるか」を書く。Design PRの事実説明は書かない。 + +### 4.3 外部依存・Secrets の扱い + +| 項目 | 内容 | リスク/対応 | +| --- | --- | --- | +| 外部依存の追加/更新 | <> | <> | +| Secrets 利用有無 | <> | <> | +| ログ/設定への機密混入対策 | <> | <> | + +### 4.4 4章の自己検証(必須) + +| チェック項目 | 合格条件 | +| --- | --- | +| Design PR差分を書いていないか | `.github/copilot/plans/*.md` や「設計ドキュメントのみ変更」を記載していない | +| 実装責務を書いているか | In-Scopeに実装責務が2件以上ある | +| 実装影響を書いているか | 4.2で `影響あり/未確定` が1件以上あり、影響内容が具体記述されている | + +--- + +## 5. アーキテクチャ設計 + +### 5.0 DI生成経路(テキスト必須) + +| 区分(記載例/追記No) | 生成/受け渡し主体 | 契約名(contract) | 具象名(impl/plugins) | 入力(契約/型/設定) | 出力(契約/型/設定) | 境界制約(禁止事項を含む) | +| --- | --- | --- | --- | --- | --- | --- | +| 記載例 | `AppProvider` | `AppDeps(contract)` | `createPublicDepsImpl(plugins)` | 設定/環境値 | `deps` 生成開始 | `page` から `deps` を生成しない | +| 記載例 | `createPublicDeps` 等のDIファクトリ | `DashboardDataSource(contract)` | `DashboardDataSourceImpl(plugins)` | Provider入力 | 具象実装入り `deps` | 具象はこの境界外へ露出しない | +| 記載例 | `AppContext.Provider` | `AppDeps(contract)` | `AppContextProviderImpl(plugins)` | `deps` | Context配布 | Context値を加工しない | +| 記載例 | `pages/.tsx` | `DashboardDataSource(contract)` | `PageBridgeImpl(pages)` | `useAppContext()` | UIへのprops | `contracts/ui/AppContext` 以外の具象import禁止 | +| 記載例 | `ui/pages/Page.tsx` | `DashboardPageProps(contract)` | `DashboardPageImpl(ui)` | 画面props | 表示 | DataSource呼び出し/DI生成をしない | +| 01 | <> | <> | <> | <> | <> | <> | +| 02 | <> | <> | <> | <> | <> | <> | +| 03 | <> | <> | <> | <> | <> | <> | +| 04 | <> | <> | <> | <> | <> | <> | +| 05 | <> | <> | <> | <> | <> | <> | + +運用補足: 本表は必須。シーケンス図より先に確定し、`5.7` の図と差分がないことを確認する。 +運用補足: 上5行の記載例は参照用として残し、必要に応じて追記行を記載する。 +運用補足: 追記行は `01` から採番し、欠番を作らない。 +運用補足: 同一主体は全章で同一表記に統一する(表記ゆれ禁止)。 +運用補足: 名称は `Xxx(contract)` と `XxxImpl(plugins)` のように「契約」と「具象」を必ず名前で区別する。 +運用補足: `5.0` / `5.7.0` / シーケンス図 / 本文で contract 名と impl 名の表記ゆれを禁止する。 +運用補足: 未確定値は `TBD(理由/決定条件/期限)` を使用し、空欄を禁止する。 + +#### 5.0.1 最小固定セット(TBD禁止) + +| 最小固定項目 | 必須記載内容 | 対応セクション | +| --- | --- | --- | +| DI単一路 | `AppProvider -> createPublicDeps -> AppContext -> page -> UI` を具体主体名で固定する | `5.0`, `5.7.0`, `5.7.2` | +| Server/Client境界 | cookie/session 読取位置とブラウザAPI可否を具体位置で固定する | `5.5.1`, `8.3` | +| import許可/禁止 | 許可import・禁止import・lint強制を具体ルールで固定する | `8.4`, `8.3` | + +運用補足: 上記3項目は `TBD(理由/決定条件/期限)` を禁止する。 +運用補足: 上記3項目の記述が未確定の場合は設計未完了として扱い、実装へ進めない。 +運用補足: 上記3項目は「文章・表・図」の3形式すべてで同一結論にそろえる。いずれか1つでも `<>` / `TBD(理由/決定条件/期限)` が残る場合は不合格。 + +### 5.1 設計判断 + +#### 5.1.1 責務分離 / データフロー(詳細) + +| 記載形式 | 選択(A/B) | +| --- | --- | +| 形式A: 箇条書き | <> | +| 形式B: テーブル | <> | + +運用補足: A/Bのどちらか一方のみ記載する。 + +形式A(箇条書き) +- <> +- <> +- <> +- <> +- <> +- <> + +形式B(テーブル) +| No. | 決定事項(実装責務単位) | 根拠 | 未確定(あれば) | +| --- | --- | --- | --- | +| 1 | <> | <> | <> | +| 2 | <> | <> | <> | +| 3 | <> | <> | <> | +| 4 | <> | <> | <> | +| 5 | <> | <> | <> | +| 6 | <> | <> | <> | + +#### 5.1.2 エッジケース / 例外系 / リトライ方針(詳細) + +| 記載形式 | 選択(A/B) | +| --- | --- | +| 形式A: 箇条書き | <> | +| 形式B: テーブル | <> | + +運用補足: A/Bのどちらか一方のみ記載する。 + +形式A(箇条書き) +- <> +- <> +- <> +- <> +- <> +- <> + +形式B(テーブル) +| No. | ケース | 方針(戻り値/表示/再試行) | 根拠 | 未確定(あれば) | +| --- | --- | --- | --- | --- | +| 1 | <> | <> | <> | <> | +| 2 | <> | <> | <> | <> | +| 3 | <> | <> | <> | <> | +| 4 | <> | <> | <> | <> | +| 5 | <> | <> | <> | <> | +| 6 | <> | <> | <> | <> | + +#### 5.1.3 Atomic Design UI部品一覧(dashboard) + +| レイヤ | UI部品名(設計上の候補) | 主責務 | 対応機能 | +| --- | --- | --- | --- | +| templates | <> | <> | <> | +| organisms | <> | <> | <> | +| organisms | <> | <> | <> | +| organisms | <> | <> | <> | +| organisms | <> | <> | <> | +| organisms | <> | <> | <> | +| organisms | <> | <> | <> | +| molecules | <> | <> | <> | +| molecules | <> | <> | <> | +| molecules | <> | <> | <> | +| molecules | <> | <> | <> | +| atoms | <> | <> | <> | +| atoms | <> | <> | <> | +| atoms | <> | <> | <> | +| atoms | <> | <> | <> | +| atoms | <> | <> | <> | + +#### 5.1.4 ログと観測性(漏洩防止を含む / 詳細) + +| 記載形式 | 選択(A/B) | +| --- | --- | +| 形式A: 箇条書き | <> | +| 形式B: テーブル | <> | + +運用補足: A/Bのどちらか一方のみ記載する。 + +形式A(箇条書き) +- <> +- <> +- <> +- <> +- <> +- <> + +形式B(テーブル) +| No. | 観点 | 方針 | 根拠 | 未確定(あれば) | +| --- | --- | --- | --- | --- | +| 1 | ログ出力内容 | <> | <> | <> | +| 2 | マスキング/非出力項目 | <> | <> | <> | +| 3 | エラー記録粒度 | <> | <> | <> | +| 4 | 監視メトリクス | <> | <> | <> | +| 5 | アラート条件 | <> | <> | <> | +| 6 | 運用確認手順 | <> | <> | <> | + +運用補足: 1セルに要約せず、実装責務単位で行を分割して記載する。 +運用補足: 「未確定(あれば)」列は `TBD(理由/決定条件/期限)` 形式で記載する。 + +### 5.2 トレードオフ + +| 判断テーマ | 案A | 案B | 採用案 | 採用理由 | 不採用理由 | +| --- | --- | --- | --- | --- | --- | +| <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | + +### 5.3 ルーティング方針の確定と移行戦略 + +| 項目 | 決定内容 | 根拠 | +| --- | --- | --- | +| 現時点の採用ルーター(pages/app) | <> | <> | +| 現方針の固定条件(理由/期限/見直し条件) | TBD(理由/決定条件/期限) | <> | +| app router 採用条件 | <> | <> | +| pages/app 併用時の境界(ディレクトリ単位でOK/NG) | <> | <> | + +### 5.4 依存カテゴリ方針(境界崩壊防止) + +| 依存カテゴリ(DataSource/Service/Adapter/Config) | 定義 | 許可レイヤ(app/src/contracts/ui/plugins) | 禁止レイヤ | +| --- | --- | --- | --- | +| DataSource | <> | <> | <> | +| Service | <> | <> | <> | +| Adapter | <> | <> | <> | +| Config | <> | <> | <> | + +運用補足: logger/feature flag/analytics/i18n/date/storage/auth は上記カテゴリに必ず分類してから配置を決める。 + +### 5.5 データ取得ライフサイクル(SSR/SSG/CSR) + +| データ種別 | 取得タイミング(SSR/SSG/CSR) | 取得場所(page/usecase/client等) | 理由 | +| --- | --- | --- | --- | +| 初期表示必須データ | <> | <> | <> | +| ユーザー操作後データ | <> | <> | <> | +| 再取得/更新データ | <> | <> | <> | + +| キャッシュ方針 | 採用有無 | ルール | +| --- | --- | --- | +| SWR | <> | <> | +| React Query | <> | <> | +| 独自キャッシュ | <> | <> | + +#### 5.5.1 Server/Client 境界固定(Next.js) + +| 対象処理 | 実行境界(Server/Client/Shared) | 実装場所(page/getServerSideProps/usecase等) | ブラウザAPI利用(可/不可) | Cookie/Session読取位置 | 禁止事項 | +| --- | --- | --- | --- | --- | --- | +| 初期表示データ取得 | <> | <> | <> | <> | <> | +| ユーザー操作イベント処理 | <> | <> | <> | <> | <> | +| 認証/認可判定 | <> | <> | <> | <> | <> | +| ローカル保存(storage等) | <> | <> | <> | <> | <> | +| ログ出力 | <> | <> | <> | <> | <> | + +運用補足: `ブラウザAPI`(window/document/localStorage等)は `Client` または `Shared(Client側のみ実行保証)` でのみ `可` を選択する。 +運用補足: `Cookie/Session読取位置` は `getServerSideProps` / `API Route` / `Client不可` など具体位置で記載する。 +運用補足: 境界違反は `8.3 実装禁止事項` と `8.4 import制約` に同じ内容で反映する。 + +### 5.6 エラーハンドリング標準形 + +| 分類(network/unauthorized/notfound/unknown) | 返却型/エラーコード | UI表示ルール | 再試行ルール | +| --- | --- | --- | --- | +| network | <> | <> | <> | +| unauthorized | <> | <> | <> | +| notfound | <> | <> | <> | +| unknown | <> | <> | <> | + +| ログ方針 | 内容 | +| --- | --- | +| 出力する情報 | <> | +| 出力しない情報(Secrets/PII) | <> | + +#### 5.6.1 エラー変換責務(例外 -> 契約エラー) + +| 変換対象 | 例外発生層 | 契約エラーへ変換する層 | 上位層へ渡す型 | 禁止事項 | +| --- | --- | --- | --- | --- | +| 外部I/O例外(HTTP/Network) | <> | providers/plugins | contractsで定義したエラー型 | page/uiで生例外を直接判定しない | +| 認可/権限エラー | <> | providers/plugins | contractsで定義したエラー型 | contractsに変換ロジックを実装しない | +| notfound/業務エラー | <> | providers/plugins | contractsで定義したエラー型 | pageで独自エラーコードを増やさない | +| unknown/予期せぬ例外 | <> | providers/plugins | contractsで定義したエラー型 | stacktrace/機密情報をUIへ渡さない | +| バリデーションエラー | <> | <> | <> | <> | + +運用補足: 例外から契約エラーへの変換責務は `providers/plugins` に固定する。 +運用補足: `contracts` はエラー型定義のみを持ち、変換ロジックを持たない。 +運用補足: `page/ui` は契約エラー型を受けて表示分岐するのみとし、生例外を扱わない。 + +### 5.7 シーケンス図(Mermaid / 複数必須) + +運用補足: 正常系・異常系で participant 名を統一し、図ごとに別名へ置換しない。 +運用補足: 図は境界保護の確認に必要な粒度へ限定し、UI内部の見た目分岐など変動が大きい詳細は書かない。 +運用補足: `ログ責務` / `例外->契約エラー変換責務` / `Server/Client境界` は本文・表・図で同一結論に統一する(矛盾禁止)。 + +| 必須項目 | 記載ルール | +| --- | --- | +| DI生成経路 | 必須(`AppProvider -> DIファクトリ -> AppContext -> Page -> UI` を明記) | +| 正常系 | 必須(最低1本) | +| 異常系 | 必須(最低2本。業務エラー系/システムエラー系) | +| パラメータ | 各呼び出しメッセージに `PARAM` を明記 | +| 戻り値 | 各応答メッセージに `RETURN` を明記 | +| エラー返却 | 各異常系で `ERROR` の返却値とハンドリング先を明記 | + +#### 5.7.0 DI生成経路(テキスト再掲 / 必須) + +| No | 開始主体 | 終了主体 | 契約名(contract) | 具象名(impl/plugins) | 経路文字列(`A -> B -> C`) | 境界チェック観点 | 対応シーケンス図ID | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 記載例 | `AppProvider` | `ui/pages/Page.tsx` | `DashboardDataSource(contract)` | `DashboardDataSourceImpl(plugins)` | `AppProvider -> createPublicDeps -> AppContext.Provider -> pages/.tsx -> ui/pages/Page.tsx` | 具象が `page/ui/contracts` に漏れていないこと | SEQ-01 | +| 01 | <> | <> | <> | <> | <> | <> | SEQ-01 | +| 02 | <> | <> | <> | <> | <> | <> | SEQ-02 | +| 03 | <> | <> | <> | <> | <> | <> | SEQ-02 | +| 04 | <> | <> | <> | <> | <> | <> | SEQ-02 | +| 05 | <> | <> | <> | <> | <> | <> | SEQ-02 | + +運用補足: 記載例の行は削除せず参照用に残す。 +運用補足: 経路文字列は `AppProvider -> DIファクトリ -> AppContext -> Page -> UI` を基準として記載する。 +運用補足: 経路文字列は `主体名` を `->` で連結した1行形式で記載する。 +運用補足: `契約名(contract)` と `具象名(impl/plugins)` は `5.0` と同じ表記を使用する。 + +#### 5.7.1 シーケンス対象一覧 + +| 図ID | 種別(正常/異常) | 起点(画面/API) | 終点(UseCase/外部I/O) | 対応要件ID(FR/NFR) | +| --- | --- | --- | --- | --- | +| SEQ-01 | 正常(DI生成経路) | <> | <> | <> | +| SEQ-02 | 異常 | <> | <> | <> | +| SEQ-03 | 異常 | <> | <> | <> | + +#### 5.7.1.1 境界整合チェック(必須) + +| 境界テーマ | 文章セクション | 表セクション | 図セクション | 整合判定(OK/NG) | +| --- | --- | --- | --- | --- | +| ログ責務(どの層で出力するか) | `5.1.4` | `5.6` | `5.7.4` | <> | +| 例外->契約エラー変換責務 | `5.1.2` | `5.6.1` | `5.7.3` | <> | +| Server/Client境界 | `5.5.1` | `8.3` | `5.7.2` | <> | + +運用補足: 3行すべて `OK` になるまで設計を確定しない。 +運用補足: `NG` の場合は、図ではなく `文章/表/図` の3点を同時に修正して再判定する。 + +#### 5.7.1.2 最小固定セット具体化チェック(必須) + +| 最小固定項目 | 文章セクション | 表セクション | 図セクション | TBD残存数(0のみ可) | +| --- | --- | --- | --- | --- | +| DI単一路(`AppProvider -> createPublicDeps -> AppContext -> page -> UI`) | `5.0.1` | `5.0` | `5.7.0`, `5.7.2` | <> | +| Server/Client境界(cookie/session・ブラウザAPI) | `5.5.1` | `5.5.1` | `5.7.2` | <> | +| import許可/禁止(lint強制含む) | `8.3` | `8.4` | `5.7.2` | <> | + +運用補足: `TBD残存数` は各項目で `0` 以外を禁止する。 +運用補足: 1件でも `0` 以外の場合は設計未完了として扱い、実装へ進めない。 + +#### 5.7.2 正常系シーケンス(必須) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 +運用補足: participant は前後機能のつながり(呼び出し元/呼び出し先/外部I/O/ログ出力先)に登場する主体をすべて列挙し、省略しない。 +運用補足: 図全体を `<>` / `TBD(理由/決定条件/期限)` で埋めることを禁止する。`PARAM` と `RETURN` はすべて具体値を記載し、最低1行は Server/Client境界の制約を明記する。 + +```mermaid +sequenceDiagram + actor User + participant Source + participant Composer + participant Context + participant Entry + participant Gateway + participant View + + Source->>Composer: execute PARAM: <> + Composer-->>Source: RETURN: <> + Source->>Context: provide PARAM: <> + User->>Entry: execute PARAM: <> + Entry->>Context: read PARAM: <> + Context-->>Entry: RETURN: <> + Entry->>Gateway: call PARAM: <> + Gateway-->>Entry: RETURN: <> + Entry->>View: render PARAM: <> + Entry-->>User: RETURN: <> +``` + +#### 5.7.3 異常系シーケンス(業務エラー) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 +運用補足: participant は前後機能のつながり(呼び出し元/呼び出し先/外部I/O/ログ出力先)に登場する主体をすべて列挙し、省略しない。 +運用補足: 図全体を `<>` / `TBD(理由/決定条件/期限)` で埋めることを禁止する。`ERROR` の契約型、変換層、戻り先を具体値で記載する。 + +```mermaid +sequenceDiagram + actor User + participant Page + participant UseCase + participant Adapter + + User->>Page: execute PARAM: <> + Page->>UseCase: invoke PARAM: <> + UseCase->>Adapter: call PARAM: <> + Adapter-->>UseCase: ERROR: <> + UseCase-->>Page: RETURN: <> + Page-->>User: RETURN: <> +``` + +#### 5.7.4 異常系シーケンス(システムエラー) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 +運用補足: participant は前後機能のつながり(呼び出し元/呼び出し先/外部I/O/ログ出力先)に登場する主体をすべて列挙し、省略しない。 +運用補足: 図全体を `<>` / `TBD(理由/決定条件/期限)` で埋めることを禁止する。`ERROR` とログ出力責務の主体を具体値で記載する。 + +```mermaid +sequenceDiagram + actor User + participant Page + participant UseCase + participant Adapter + participant Logger + + User->>Page: execute PARAM: <> + Page->>UseCase: invoke PARAM: <> + UseCase->>Adapter: call PARAM: <> + Adapter-->>UseCase: ERROR: <> + UseCase->>Logger: write PARAM: <> + UseCase-->>Page: RETURN: <> + Page-->>User: RETURN: <> +``` + +### 5.8 処理フロー図(メソッドレベル / 複数必須) + +| 必須項目 | 記載ルール | +| --- | --- | +| 対象メソッド数 | 必須(最低3メソッド) | +| 分岐 | 各メソッドで正常/異常分岐を明記 | +| 入出力 | 各メソッドの入力/出力を明記 | +| 例外処理 | 例外時の戻り値または伝播先を明記 | + +運用補足: 図は境界維持に効くメソッドを優先し、少なくとも2本は `provider/context/page/contracts` の境界に関わるメソッドを対象にする。 +運用補足: UIの細かな表示分岐のみを図示することは禁止。境界/契約に影響する分岐を記載する。 +運用補足: メソッドフロー図を全件 `<>` / `TBD(理由/決定条件/期限)` で埋めることを禁止する。最低3本は `INPUT` / `PROCESS` / `RETURN` を具体値で記載する。 + +#### 5.8.1 メソッド一覧 + +| 図ID | メソッド名 | 層(page/usecase/adapter等) | 対応要件ID(FR/NFR) | +| --- | --- | --- | --- | +| FLOW-01 | <> | <> | <> | +| FLOW-02 | <> | <> | <> | +| FLOW-03 | <> | <> | <> | +| FLOW-04 | <> | <> | <> | +| FLOW-05 | <> | <> | <> | +| FLOW-06 | <> | <> | <> | +| FLOW-07 | <> | <> | <> | +| FLOW-08 | <> | <> | <> | +| FLOW-09 | <> | <> | <> | +| FLOW-10 | <> | <> | <> | + +運用補足: メソッド名の全件 `<>` / `TBD(理由/決定条件/期限)` は禁止する。最低3件は具体メソッド名を記載する。 + +#### メソッドフロー(FLOW-01) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 +運用補足: 図全体を `<>` / `TBD(理由/決定条件/期限)` で埋めることを禁止する。`INPUT` / `PROCESS` / `RETURN` の3要素は具体値で記載する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{VALIDATION} + C -->|NG| D[RETURN ERROR: <>] + C -->|OK| E[PROCESS: <>] + E --> F[RETURN: <>] +``` + +#### メソッドフロー(FLOW-02) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{EXTERNAL CALL} + C -->|FAIL| D[HANDLE ERROR: <>] + C -->|SUCCESS| E[TRANSFORM: <>] + E --> F[RETURN: <>] +``` + +#### メソッドフロー(FLOW-03) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-04) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-05) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-06) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-07) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-08) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-09) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + + +#### メソッドフロー(FLOW-10) + +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +flowchart TD + A[START METHOD: <>] --> B[INPUT: <>] + B --> C{CONDITION} + C -->|PATH1| D[PROCESS1: <>] + C -->|PATH2| E[PROCESS2: <>] + D --> F[RETURN: <>] + E --> F +``` + +--- + +## 6. 契約仕様(Interface Contract) + +### 6.0 DIP固定前提(Plugin型アーキテクチャ) + +| 項目 | 固定方針 | +| --- | --- | +| Composition Root | `AppProvider` のみで依存解決する | +| `contracts` の責務 | interface/type のみ定義し、具象実装を含めない | +| 具象実装の配置 | `plugins` または `providers/*` のDI境界内に限定する | +| `page` / `ui` の責務 | 契約に依存し、具象依存を直接 import しない | + +運用補足: 契約定義が曖昧な場合は実装を開始しない。先にこの章を確定させる。 + +### 6.1 入出力契約(API/関数/UseCase) + +| ID | 入口(画面/API/関数) | 入力 | 出力 | エラー | 備考 | +| --- | --- | --- | --- | --- | --- | +| IFC-01 | <> | <> | <> | <> | <> | +| IFC-02 | <> | <> | <> | <> | <> | + +運用補足: IDは `IFC-01` 形式の連番。入口ごとに採番する。 + +### 6.2 型/DTO/スキーマ + +| ID | 対象 | 変更内容(追加/変更/削除) | 後方互換 | +| --- | --- | --- | --- | +| TYPE-01 | <> | <> | <> | +| TYPE-02 | <> | <> | <> | + +運用補足: IDは `TYPE-01` 形式の連番。変更内容は `追加` / `変更` / `削除` を使用する。 + +### 6.3 契約インターフェース定義(実装エンジニア向け固定案) + +#### 6.3.1 ページ別DataSource契約 + +| No. | 契約ファイル(`packages/contracts/src/pages/*.ts`) | interface名 | メソッド署名(戻り値まで) | 備考 | +| --- | --- | --- | --- | --- | +| 1 | <> | <> | <> | <> | +| 2 | <> | <> | <> | <> | +| 3 | <> | <> | <> | <> | + +#### 6.3.2 ドメインクラス図(Mermaid classDiagram) + +| 図ID(固定: CLS-01) | ドメイン | 対応契約ファイル | 対応要件ID(FR/NFR) | +| --- | --- | --- | --- | +| CLS-01 | <> | <> | <> | +| CLS-02 | <> | <> | <> | +| CLS-03 | <> | <> | <> | +| CLS-04 | <> | <> | <> | +| CLS-05 | <> | <> | <> | + +##### ドメインレベルのクラス図(CLS-01) + +運用補足: ドメイン単位でクラスをグルーピングし、関連(集約/参照)と主要フィールドを明記する。 +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +classDiagram + direction TB + %% TODO: 1つのクラス図で1ドメインを表現 + %% class DashboardPageData { + %% +header: DashboardHeaderModel + %% +projects: DashboardProjectModel[] + %% } + %% class DashboardProjectModel { + %% +id: string + %% +status: ProjectStatus + %% } + %% DashboardPageData --> DashboardProjectModel +``` + +##### ドメインレベルのクラス図(CLS-02) + +運用補足: ドメイン単位でクラスをグルーピングし、関連(集約/参照)と主要フィールドを明記する。 +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +classDiagram + direction TB + %% TODO: 1つのクラス図で1ドメインを表現 + %% class DashboardPageData { + %% +header: DashboardHeaderModel + %% +projects: DashboardProjectModel[] + %% } + %% class DashboardProjectModel { + %% +id: string + %% +status: ProjectStatus + %% } + %% DashboardPageData --> DashboardProjectModel +``` + +##### ドメインレベルのクラス図(CLS-03) + +運用補足: ドメイン単位でクラスをグルーピングし、関連(集約/参照)と主要フィールドを明記する。 +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +classDiagram + direction TB + %% TODO: 1つのクラス図で1ドメインを表現 + %% class DashboardPageData { + %% +header: DashboardHeaderModel + %% +projects: DashboardProjectModel[] + %% } + %% class DashboardProjectModel { + %% +id: string + %% +status: ProjectStatus + %% } + %% DashboardPageData --> DashboardProjectModel +``` + +##### ドメインレベルのクラス図(CLS-04) + +運用補足: ドメイン単位でクラスをグルーピングし、関連(集約/参照)と主要フィールドを明記する。 +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +classDiagram + direction TB + %% TODO: 1つのクラス図で1ドメインを表現 + %% class DashboardPageData { + %% +header: DashboardHeaderModel + %% +projects: DashboardProjectModel[] + %% } + %% class DashboardProjectModel { + %% +id: string + %% +status: ProjectStatus + %% } + %% DashboardPageData --> DashboardProjectModel +``` + +##### ドメインレベルのクラス図(CLS-05) + +運用補足: ドメイン単位でクラスをグルーピングし、関連(集約/参照)と主要フィールドを明記する。 +運用補足: Mermaidのラベルでは半角括弧/半角カギ括弧を使わず、全角の `( )[ ]{ }` を使用する。 + +```mermaid +classDiagram + direction TB + %% TODO: 1つのクラス図で1ドメインを表現 + %% class DashboardPageData { + %% +header: DashboardHeaderModel + %% +projects: DashboardProjectModel[] + %% } + %% class DashboardProjectModel { + %% +id: string + %% +status: ProjectStatus + %% } + %% DashboardPageData --> DashboardProjectModel +``` + +#### 6.3.3 ドメイン別モデル定義(省略不可) + +運用補足: 論理名ではなく、コード上の物理名(実際の型名/プロパティ名)で記載する。 +運用補足: `必須フィールド` のような要約列は禁止。全プロパティを行単位で列挙する。 +運用補足: `any` / `unknown` / `object` の曖昧型は禁止。複合型は `6.3.3.3` で展開する。 + +##### 6.3.3.1 モデル一覧 + +| ドメイン | エンティティ名(型名) | 区分(Entity/ValueObject/DTO) | 用途 | +| --- | --- | --- | --- | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | + +##### 6.3.3.2 プロパティ詳細定義(全項目を行で列挙) + +| ドメイン | エンティティ名 | プロパティ物理名(path可) | TypeScript型(完全表記) | 利用コンポーネント/型定義名(ui) | 必須(Y/N) | Nullable(Y/N) | 説明 | 例 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | +| <> | <> | <> | <> | <> | <> | <> | <> | <> | + +運用補足: `利用コンポーネント/型定義名(ui)` には `DashboardPage` / `ProjectList` などの利用先コンポーネント名、または `ProjectListProps` など ui 側型定義名を必ず記載する。 +運用補足: `DashboardPageData` の部分集合をそのまま渡す場合も、再定義する場合も、どちらの方針かが判別できる名前で統一する。 + +##### 6.3.3.3 複合型/ネスト型の展開定義(Node.js向け) + +| 型名 | 種別(object/array/union/tuple/map) | 定義(省略不可) | 使用箇所 | +| --- | --- | --- | --- | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | +| <> | <> | <> | <> | + +#### 6.3.4 列挙値/リテラル制約 + +| No. | 対象型 | 制約値(union literal) | 用途 | +| --- | --- | --- | --- | +| 1 | <> | <> | <> | +| 2 | <> | <> | <> | +| 3 | <> | <> | <> | + +#### 6.3.5 契約互換性ルール + +| 項目 | ルール | +| --- | --- | +| 破壊的変更の扱い | <> | +| Optional追加の扱い | <> | +| 型名変更/移動の扱い | <> | +| 実装側(plugins/providers)への影響確認手順 | <> | + +--- + +## 7. データ設計(必要な場合のみ) + +| 項目 | 内容 | 互換性/移行 | +| --- | --- | --- | +| スキーマ変更 | <> | <> | +| マイグレーション方針 | <> | <> | +| 既存データ影響 | <> | <> | +| ロールバック方針 | <> | <> | + +--- + +## 8. 実装指示(製造Agent向け) + +### 8.1 変更予定ファイル一覧(必須) + +| No. | パス | 区分(app/src/contracts/ui/plugins/other) | 変更タイプ(追加/変更/削除) | 実装内容(具体) | 完了条件 | +| --- | --- | --- | --- | --- | --- | +| 1 | path/to/target.ts | <> | <> | <> | <> | +| 2 | path/to/target.ts | <> | <> | <> | <> | +| 3 | path/to/target.ts | <> | <> | <> | <> | +| 4 | path/to/target.ts | <> | <> | <> | <> | +| 5 | path/to/target.ts | <> | <> | <> | <> | +| 6 | path/to/target.ts | <> | <> | <> | <> | +| 7 | path/to/target.ts | <> | <> | <> | <> | +| 8 | path/to/target.ts | <> | <> | <> | <> | +| 9 | path/to/target.ts | <> | <> | <> | <> | +| 10 | path/to/target.ts | <> | <> | <> | <> | + +運用補足: 区分は `app` / `src` / `contracts` / `ui` / `plugins` / `other` のいずれか1つ。変更タイプは `追加` / `変更` / `削除` のみ。 +運用補足: `createPublicDeps.ts` などモックデータ実装を含む行では、実装内容にデータバリエーション要件を明記する(例: 各一覧は最低3件以上、`status` は `open` と `closed` を最低1件ずつ含める)。 + +### 8.2 実装手順(順序付き) + +| 手順 | 作業内容 | 対象ファイル/モジュール | 完了条件 | +| --- | --- | --- | --- | +| 1 | <> | <> | <> | +| 2 | <> | <> | <> | +| 3 | <> | <> | <> | + +運用補足: 手順は実行順で記載し、各手順に完了条件を必ず設定する。 + +### 8.3 実装禁止事項(ガードレール) + +| 項目 | 内容 | 根拠 | +| --- | --- | --- | +| 禁止事項-1 | <> | <> | +| 禁止事項-2 | <> | <> | +| 禁止事項-3 | <> | <> | +| 禁止事項-4 | <> | <> | +| 禁止事項-5 | <> | <> | +| 禁止事項-6 | <> | <> | +| 禁止事項-7 | <> | <> | +| 禁止事項-8 | <> | <> | + +### 8.4 import制約の自動化 + +| 項目 | 設定内容 | 検証方法 | +| --- | --- | --- | +| `no-restricted-imports` 方針 | `pages` / `ui` から `providers/plugins` 具象への直接importを禁止する | ESLint | +| path alias 方針(`@contracts/@ui/@app` など) | `packages/contracts` 参照は相対パス禁止。必ず `@upstream/contracts` を使用(例: `import type { DashboardDataSource } from "@upstream/contracts/pages/dashboard";`) | ESLint + Typecheck | +| UI export 方針(default export禁止) | UIコンポーネントは Named Export(`export const Xxx = ...`)に統一し、`index.ts` 経由 import を禁止して直接ファイル参照する | ESLint | +| CIでの強制(lint必須/違反時fail) | `no-restricted-imports` / default export禁止 / `index.ts` 経由禁止の違反を lint fail として PR をブロックする | GitHub Actions | + +--- + +## 9. テスト実装計画 + +### 9.1 テストケース + +Unitテストを完全網羅すること + +| 区分(正常/例外/境界/回帰) | パターン名 | 対象 | シナリオ | 期待結果 | +| --- | --- | --- | --- | --- | +| 正常 | <> | <> | <> | <> | +| 正常 | <> | <> | <> | <> | +| 正常 | <> | <> | <> | <> | +| 正常 | <> | <> | <> | <> | +| 正常 | <> | <> | <> | <> | +| 例外 | <> | <> | <> | <> | +| 例外 | <> | <> | <> | <> | +| 例外 | <> | <> | <> | <> | +| 例外 | <> | <> | <> | <> | +| 例外 | <> | <> | <> | <> | +| 境界 | <> | <> | <> | <> | +| 境界 | <> | <> | <> | <> | +| 境界 | <> | <> | <> | <> | +| 境界 | <> | <> | <> | <> | +| 境界 | <> | <> | <> | <> | +| 回帰 | <> | <> | <> | <> | +| 回帰 | <> | <> | <> | <> | +| 回帰 | <> | <> | <> | <> | +| 回帰 | <> | <> | <> | <> | +| 回帰 | <> | <> | <> | <> | + +運用補足: テストIDは使用しない。区分ごとのパターン網羅を優先する。 + +| 網羅チェック | 判定(Y/N) | 根拠 | +| --- | --- | --- | +| 正常パターンを網羅している | <> | <> | +| 例外パターンを網羅している | <> | <> | +| 境界パターンを網羅している | <> | <> | +| 回帰パターンを網羅している | <> | <> | + + + +--- + +## 10. オープン課題 / ADR + +| 論点 | 現状 | 決定期限/担当 | ADR要否(要/不要/TBD) | +| --- | --- | --- | --- | +| <> | <> | <> | <> | +| <> | <> | <> | <> | + +運用補足: ADR要否は `要` / `不要` / `TBD`。`TBD` の場合は「決定期限/担当」を `TBD(理由/決定条件/期限)` 形式で記載する。 + +### 10.1 TBD回収トラッキング(必須) + +| TBD論点 | 現在の記載箇所(章/項目) | 解決ゲート(必須) | BLOCKER(Yes/No) | RESOLVE_IN(必須) | DEFAULT/ASSUMPTION(任意) | ADR記録先(必要時) | +| --- | --- | --- | --- | --- | --- | --- | +| <> | <> | GATE: 契約(contracts)確定前 | BLOCKER: <> | RESOLVE_IN: <> | DEFAULT/ASSUMPTION: <> | <> | +| <> | <> | GATE: 実装PR作成前 | BLOCKER: <> | RESOLVE_IN: <> | DEFAULT/ASSUMPTION: <> | <> | +| <> | <> | GATE: マージ前 | BLOCKER: <> | RESOLVE_IN: <> | DEFAULT/ASSUMPTION: <> | <> | +| <> | <> | GATE: リリースタグ作成前 | BLOCKER: <> | RESOLVE_IN: <> | DEFAULT/ASSUMPTION: <> | <> | +| <> | <> | GATE: <> | BLOCKER: <> | RESOLVE_IN: <> | DEFAULT/ASSUMPTION: <> | <> | + +運用補足: 本表には、本文中で `TBD(理由/決定条件/期限)` を使った項目をすべて列挙する。 +運用補足: `解決ゲート` は日付ではなくイベントで記載する(例: `GATE: 実装PR作成前`)。 +運用補足: `BLOCKER: Yes` の項目は codingAgent の作業開始禁止。`BLOCKER: No` の場合のみ暫定値で進行可能。 +運用補足: `RESOLVE_IN` は章/図/ファイルパスを明示する(例: `6.1`, `5.7.2`, `contracts/pages/dashboard.ts`)。 +運用補足: `DEFAULT/ASSUMPTION` は未解決時の暫定値を1行で記載する。 +運用補足: ADRが必要な論点は `70-adr/` の記録先を明記する。 + +--- + +## 11. 新規ページ追加テンプレ(設計規約) + +### 11.1 docs 必須項目 + +| 項目 | 記載内容 | +| --- | --- | +| `docs/pages/.md` の必須見出し | <> | +| 受入条件リンク(FR/NFR/T) | <> | + +### 11.2 contracts 必須項目 + +| 項目 | 記載内容 | +| --- | --- | +| `contracts/pages/.ts` の必須型 | <> | +| 入出力/エラー契約との対応 | <> | + +### 11.3 ui 必須項目 + +| 項目 | 記載内容 | +| --- | --- | +| `ui/pages//Page.tsx` の責務 | <> | +| 禁止事項(I/O直接実装など) | <> | + +### 11.4 app page 必須項目 + +| 項目 | 記載内容 | +| --- | --- | +| `pages/.tsx` または `app//page.tsx` の責務 | <> | +| 禁止事項チェック(import/ロジック/例外) | <> | + +--- diff --git a/.github/copilot/80-templates/pr-checklist.md b/.github/copilot/80-templates/pr-checklist.md index a717e6a..acc500c 100644 --- a/.github/copilot/80-templates/pr-checklist.md +++ b/.github/copilot/80-templates/pr-checklist.md @@ -1,5 +1,12 @@ -- [ ] 目的とスコープが明記され、plan から逸脱していない -- [ ] 変更点を要約し、互換性リスクとロールバック方法を記載 -- [ ] テスト結果(コマンドと結果)を記載し、必要なテストを追加 -- [ ] CI 品質ゲート(format / lint / typecheck / test / security)が全て緑 -- [ ] ドキュメント(README/仕様/ADR)が必要に応じて更新済み +### [設計チェック] +- [ ] PR本文に目的とスコープが明記され、plan から逸脱していない +- [ ] PR本文に変更点を要約し、互換性リスクとロールバック方法を記載 +- [ ] 設計書は実装イシューの実行指示書として完結しており、追加の設計・検討を要求する記述を含んでいない +- [ ] テストは一切実行しない。念のためを含め、ローカル実行・追加実行・再実行は禁止とする(CI の自動検証のみを許可) +- [ ] PR本文に判断が必要な点・未確定事項・今後の課題を明記している +- [ ] ドキュメント(README / 仕様 / ADR)が必要に応じて更新済み +### [コーディング(製造)チェック] +- [ ] 処理は責務ごとに分割し、再利用可能な単位として関数化(手続化)されている +- [ ] 処理の意図が分かるよう、コード内に日本語の1行コメントを適切に記載している +- [ ] 関数・クラス・定数には、日本語のdocコメントで役割と責務を記載している +- [ ] テストは一切実行しない。念のためを含め、ローカル実行・追加実行・再実行は禁止とする(CI の自動検証のみを許可) \ No newline at end of file diff --git a/.github/copilot/90-research/index.md b/.github/copilot/90-research/index.md new file mode 100644 index 0000000..27e2bab --- /dev/null +++ b/.github/copilot/90-research/index.md @@ -0,0 +1,5 @@ +# このフォルダは、調査報告書フォルダです + +* ファイル名はイシュー本文の成果物に記載のファイル名で作成してください。 +* ファイル名のルールは`(イシューNo)-(トピック名).md`としています。 +* イシューNoが不明の場合は`00`としてください。 diff --git a/.github/copilot/plans/index.md b/.github/copilot/plans/index.md new file mode 100644 index 0000000..7896980 --- /dev/null +++ b/.github/copilot/plans/index.md @@ -0,0 +1,5 @@ +# このフォルダは、詳細設計書フォルダです + +* ファイル名はイシュー本文の成果物に記載のファイル名で作成してください。 +* ファイル名のルールは`(イシューNo)-(トピック名).md`としています。 +* イシューNoが不明の場合は`00`としてください。 diff --git a/.github/issues/dependabot-yml-enable.md b/.github/issues/dependabot-yml-enable.md new file mode 100644 index 0000000..edc6d9f --- /dev/null +++ b/.github/issues/dependabot-yml-enable.md @@ -0,0 +1,31 @@ +--- +name: 'dependabot-yml-enable' +title: '🔧 Dependabot 設定ファイルの有効化' +labels: 'automation' +assignees: '' +--- +# タスク: `.github/dependabot.yml` の有効化 + +## 背景 +現在、`.github/dependabot.yml` は全体がコメントアウトされており、Dependabot が動作していない。 + +## 目的 +Docker(本番/開発)および pip 依存関係の自動更新を有効化する。 + +## 作業内容 +- `.github/dependabot.yml` のコメントアウトを解除し、有効な YAML として保存する +- 設定内容を以下で有効化する + - `package-ecosystem: docker`(`/docker/prod`) + - `package-ecosystem: docker`(`/docker/dev`) + - `package-ecosystem: pip`(`/src`) +- `schedule.interval` はすべて `weekly` +- `open-pull-requests-limit` はすべて `5` + +## 受け入れ条件 +- `.github/dependabot.yml` が有効な YAML として解釈できる +- GitHub 上で Dependabot 設定エラーが発生しない +- 上記3エコシステムの更新設定が反映される + +## 完了条件 +- PR を作成し、CI 品質ゲート(lint / typecheck / test / security)を満たす +- 変更内容を PR に明記する \ No newline at end of file diff --git a/.github/memo.txt b/.github/memo.txt new file mode 100644 index 0000000..77f5554 --- /dev/null +++ b/.github/memo.txt @@ -0,0 +1,34 @@ +workflows + githubワークフローの定義ファイルを置く場所です +assets + reeadme.mdファイルが参照する画像ファイル等を置くフォルダです +CODEOWNERS + コードに関する PR が作成された際にそのオーナーを自動でアサインすることができます。 + また、Branch protection rule と組み合わせ、 + オーナーの承認がなければ PR をマージできないようにすることも可能です。 + 特定のディレクトリやファイル拡張子に対して細かくオーナーを設定することができ、 + オーナーには GitHub のユーザー名やチーム名を指定できます。 +dependabot.yml + Dependabot は、GitHub が提供する依存パッケージの自動更新サービスです。 + また、パッケージの脆弱性を検知しセキュリティアップデートを自動で適用したり、アラートを送信することも可能です。 + 各機能は公式にはそれぞれ Dependabot version updates、Dependabot security updates、Dependabot alerts と呼ばれています。 +CODE_OF_CONDUCT.md + 行動規範の設定 + プロジェクトの行動規範を明示することもできます。 + 行動規範は、OSS 開発という場における公共性を担保するために重要となります。 +CONTRIBUTING.md + コントリビューションガイドラインの設定 + プロジェクトへの貢献の仕方、たとえばコミットメッセージのフォーマットや、 + コードのスタイルガイドなどを記述したコントリビューションガイドラインを作成することで、 + リポジトリに対する PR や Issue に関連するコミュニケーションを円滑にすることができます。 +FUNDING.yml + スポンサーの募集 + .github/FUNDING.yml ファイルを作成し、プロジェクトのスポンサーを募集することができます。 + このファイルには、資金の獲得に使用するプラットフォームと、対象となるユーザー名を記述します。 +PULL_REQUEST_TEMPLATE + プルリクエストテンプレート +SECURITY.md + リポジトリのセキュリティ脆弱性を報告するための手順 +SUPPORT.md + サポート情報の提示 + ユーザーに対してどのようなサポートが可能かどうかを提示することも可能です。 diff --git a/.github/scaffold/nextjs/config/.markdownlint.json b/.github/scaffold/nextjs/config/.markdownlint.json new file mode 100644 index 0000000..26593a2 --- /dev/null +++ b/.github/scaffold/nextjs/config/.markdownlint.json @@ -0,0 +1,4 @@ +{ + "MD013": false, + "MD033": false +} diff --git a/.github/scaffold/nextjs/config/.stylelintrc.json b/.github/scaffold/nextjs/config/.stylelintrc.json new file mode 100644 index 0000000..978d3c5 --- /dev/null +++ b/.github/scaffold/nextjs/config/.stylelintrc.json @@ -0,0 +1,7 @@ +{ + "rules": { + "block-no-empty": true, + "color-no-invalid-hex": true, + "property-no-unknown": true + } +} diff --git a/.github/scaffold/nextjs/config/.textlintignore b/.github/scaffold/nextjs/config/.textlintignore new file mode 100644 index 0000000..1775b02 --- /dev/null +++ b/.github/scaffold/nextjs/config/.textlintignore @@ -0,0 +1 @@ +.github/copilot/05-structure/monorepo.md.backup0102.md diff --git a/.github/scaffold/nextjs/config/.textlintrc b/.github/scaffold/nextjs/config/.textlintrc new file mode 100644 index 0000000..ed0c691 --- /dev/null +++ b/.github/scaffold/nextjs/config/.textlintrc @@ -0,0 +1,21 @@ +{ + "plugins": ["markdown"], + "rules": { + "preset-ja-technical-writing": { + "ja-no-mixed-period": false, + "ja-no-redundant-expression": false, + "ja-no-successive-word": false, + "max-kanji-continuous-len": false, + "max-comma": false, + "max-ten": false, + "no-doubled-joshi": false, + "no-exclamation-question-mark": false, + "no-mix-dearu-desumasu": false, + "no-unmatched-pair": false, + "sentence-length": false + }, + "prh": { + "rulePaths": ["prh.yml"] + } + } +} diff --git a/.github/scaffold/nextjs/config/eslint.config.mjs b/.github/scaffold/nextjs/config/eslint.config.mjs new file mode 100644 index 0000000..abc68a6 --- /dev/null +++ b/.github/scaffold/nextjs/config/eslint.config.mjs @@ -0,0 +1,21 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "storybook-static/**", + "next-env.d.ts", + // Mock is not part of the release build; linted separately in mock-ci. + "mock/**", + ]), +]); + +export default eslintConfig; diff --git a/.github/scaffold/nextjs/config/eslint.config.mock.mjs b/.github/scaffold/nextjs/config/eslint.config.mock.mjs new file mode 100644 index 0000000..c4afd44 --- /dev/null +++ b/.github/scaffold/nextjs/config/eslint.config.mock.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextTs from "eslint-config-next/typescript"; + +// ESLint configuration for mock directory linting. +// Deliberately separate from eslint.config.mjs (production), which excludes mock/. +// Uses TypeScript rules only; Next.js-specific rules from core-web-vitals are omitted +// because mock/v1/web is a React Router / Vite application. +const eslintMockConfig = defineConfig([ + ...nextTs, + globalIgnores([ + "mock/**/node_modules/**", + "mock/**/.react-router/**", + "mock/**/build/**", + "mock/**/.next/**", + ]), +]); + +export default eslintMockConfig; diff --git a/.github/scaffold/nextjs/config/next-env.d.ts b/.github/scaffold/nextjs/config/next-env.d.ts new file mode 100644 index 0000000..c4b7818 --- /dev/null +++ b/.github/scaffold/nextjs/config/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/dev/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/.github/scaffold/nextjs/config/next.config.ts b/.github/scaffold/nextjs/config/next.config.ts new file mode 100644 index 0000000..68a6c64 --- /dev/null +++ b/.github/scaffold/nextjs/config/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "standalone", +}; + +export default nextConfig; diff --git a/.github/scaffold/nextjs/config/playwright.config.ts b/.github/scaffold/nextjs/config/playwright.config.ts new file mode 100644 index 0000000..606fcf7 --- /dev/null +++ b/.github/scaffold/nextjs/config/playwright.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "tests/e2e", + timeout: 30_000, + use: { + baseURL: "http://localhost:4100", + }, + webServer: { + command: "npm run build && npm run start -- --port 4100", + port: 4100, + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, +}); diff --git a/.github/scaffold/nextjs/config/postcss.config.mjs b/.github/scaffold/nextjs/config/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/.github/scaffold/nextjs/config/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/.github/scaffold/nextjs/config/prh.yml b/.github/scaffold/nextjs/config/prh.yml new file mode 100644 index 0000000..d177a57 --- /dev/null +++ b/.github/scaffold/nextjs/config/prh.yml @@ -0,0 +1,8 @@ +version: 1 +rules: + - expected: ユーザー + patterns: + - /ユーザ(?!ー)/ + - expected: インターフェース + patterns: + - インタフェース diff --git a/.github/scaffold/nextjs/config/tsconfig.json b/.github/scaffold/nextjs/config/tsconfig.json new file mode 100644 index 0000000..2f390d4 --- /dev/null +++ b/.github/scaffold/nextjs/config/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "target": "ES2017", + "baseUrl": ".", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"], + "@app/*": ["src/*"], + "@contracts/*": ["packages/contracts/src/*"], + "@plugins/*": ["packages/plugins/src/*"], + "@ui/*": ["packages/ui/src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules", "mock/**"] +} diff --git a/.github/scaffold/nextjs/config/vitest.config.ts b/.github/scaffold/nextjs/config/vitest.config.ts new file mode 100644 index 0000000..b3c5ac8 --- /dev/null +++ b/.github/scaffold/nextjs/config/vitest.config.ts @@ -0,0 +1,19 @@ +import tsconfigPaths from "vite-tsconfig-paths"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [tsconfigPaths({ projects: ["./tsconfig.json"] })], + test: { + environment: "jsdom", + include: [ + "app/**/?(*.)+(test).[tj]s?(x)", + "src/**/?(*.)+(test).[tj]s?(x)", + // Reserved for future `packages/**` monorepo structure (see .github/copilot/40-testing-strategy.md) + "packages/**/?(*.)+(test).[tj]s?(x)", + ], + setupFiles: ["./vitest.setup.ts"], + coverage: { + provider: "v8", + }, + }, +}); diff --git a/.github/scaffold/nextjs/config/vitest.setup.ts b/.github/scaffold/nextjs/config/vitest.setup.ts new file mode 100644 index 0000000..f149f27 --- /dev/null +++ b/.github/scaffold/nextjs/config/vitest.setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest"; diff --git a/.github/scaffold/nextjs/instructions/nextjs.upstream.instructions.md.backup b/.github/scaffold/nextjs/instructions/nextjs.upstream.instructions.md.backup new file mode 100644 index 0000000..58879a9 --- /dev/null +++ b/.github/scaffold/nextjs/instructions/nextjs.upstream.instructions.md.backup @@ -0,0 +1,19 @@ +--- +name: Next.js upstream architecture rules +description: Next.js plugin architecture SSOT +applyTo: + - "app/**/*.{tsx,ts}" + - "src/**/*.{tsx,ts}" +--- +- AppProvider を唯一の Composition Root とし、依存性注入はここでのみ行う。 +- DI 起点は Next.js のエントリ `app/layout.tsx` に固定する。 +- `app/*/page.tsx` は DI コンテナを生成せず、`AppContext` から依存を受け取り UI へ渡す薄いラッパーとして実装する。 +- contracts は interface/type のみを定義し、実装や具体的な API 依存を持ち込まない。インターフェース名は `ドメイン名 + DataSource/Repository/Service`(例: `GanttDataSource`)など、SSOT で定めたパターンに従う。 +- contracts から providers などの実装を import しない。 +- `app/*/page.tsx` は `packages/contracts/*` / `packages/ui/*` / `AppContext` のみを参照可能とする。 +- Client 実行が必要なファイルは先頭に `"use client"` を明記し、`"use client"` なしのファイルで browser API と client-only hooks を使用しない。 +- cookie/session の読取は Server 実行境界(Route Handler / Server Component)でのみ行う。 +- public 完成実装の差し替え点は `packages/plugins` に集約する。 +- `app/` / `src/` では `index.ts` 経由の再エクスポート import を禁止し、定義ファイルを直接参照する。 +- public リポジトリに private 実装や `private/` ディレクトリを追加しない。 +- 上記の規約は ESLint の `no-restricted-imports` で `private/` への import を禁止することで技術的に担保する予定である(注:ESLint 設定は別 Issue で実装予定)。 diff --git a/.github/scaffold/nextjs/instructions/react.upstream.instructions.md.backup b/.github/scaffold/nextjs/instructions/react.upstream.instructions.md.backup new file mode 100644 index 0000000..fa62b81 --- /dev/null +++ b/.github/scaffold/nextjs/instructions/react.upstream.instructions.md.backup @@ -0,0 +1,10 @@ +--- +name: React upstream DI rules +description: React components must rely on contracts and AppProvider DI +applyTo: + - "app/**/*.{tsx,ts}" +--- +- `app/*/page.tsx` は `packages/contracts/*` と `packages/ui/*` と `AppContext` を参照可能とする。 +- 依存解決は AppProvider のみで行い、コンポーネント内部で具象実装を生成しない。 +- `packages/ui` は props を受けて描画に専念し、具象I/O実装を持たない。 +- `private/` ディレクトリを追加しない。 diff --git a/.github/scaffold/nextjs/instructions/tests.backend.instructions.md.backup b/.github/scaffold/nextjs/instructions/tests.backend.instructions.md.backup new file mode 100644 index 0000000..124ec15 --- /dev/null +++ b/.github/scaffold/nextjs/instructions/tests.backend.instructions.md.backup @@ -0,0 +1,14 @@ +--- +name: Backend tests rules +description: バックエンド(ユースケース/Route Handler/外部API連携)のテスト実務ルール +applyTo: + - "packages/**/*.{ts,tsx}" + - "src/**/*.{ts,tsx}" + - "app/**/route.{ts,tsx}" + - "app/**/*.{test,spec}.{ts,tsx}" +--- +- ユースケース/ドメイン(`packages/**` の純TSロジック)は Vitest のユニットテストで検証する。 +- HTTP境界(Route Handlers)は「ハンドラを直接呼ぶ統合テスト」を実施し、テストDBまたはトランザクションで検証する。 +- 外部API連携は `nock` / `undici mock` / MSW(node)のいずれかでモックし、失敗/遅延/リトライを含めて検証する。 +- E2E(Playwright)は「UI + Route + DB」を通すスモークで最終保証を行う。 +- カバレッジは `packages/` と `src/` を中心に測定し、`app/` はテスト対象境界を明記する(例: Server Component は数値目標の対象外)。 diff --git a/.github/scaffold/nextjs/instructions/tests.frontend.instructions.md.backup b/.github/scaffold/nextjs/instructions/tests.frontend.instructions.md.backup new file mode 100644 index 0000000..ce4b989 --- /dev/null +++ b/.github/scaffold/nextjs/instructions/tests.frontend.instructions.md.backup @@ -0,0 +1,15 @@ +--- +name: Frontend tests rules +description: フロントエンド(Next.js/React)のテスト実務ルール +applyTo: + - "**/*.{test,spec}.{ts,tsx}" + - "packages/**/*.{ts,tsx}" + - "src/**/*.{ts,tsx}" + - "app/**/*.{ts,tsx}" +--- +- Storybook を UI コンポーネントの確認基盤として採用し、状態ごとに Story を管理する。 +- UIユニットテストは Vitest + React Testing Library を標準とし、DOM環境は jsdom(または happy-dom)を採用する。 +- API通信を伴うUIのテストは MSW でHTTPをモックし、成功/失敗/遅延/空を最低1ケースずつ含める。 +- E2E は Playwright を採用し、主要導線のスモークテストを最小セットで維持する。 +- Next.js依存(`next/navigation`, `next/image`, `next/link`)は共通モック/スタブへ集約し、各テストに重複実装しない。 +- カバレッジは `packages/` と `src/` を中心に測定し、`app/` はテスト対象境界を明記する(例: Server Component は数値目標の対象外)。 diff --git a/.github/scaffold/nextjs/workflows/ci-nextjs.yml b/.github/scaffold/nextjs/workflows/ci-nextjs.yml new file mode 100644 index 0000000..021c60a --- /dev/null +++ b/.github/scaffold/nextjs/workflows/ci-nextjs.yml @@ -0,0 +1,91 @@ +name: CI(Next.js 基本ゲート / Node 24) + +on: + pull_request: + paths-ignore: + - "mock/**" + - "**/*.md" + push: + branches: [main] + paths-ignore: + - "mock/**" + - "**/*.md" + workflow_dispatch: + inputs: + runTests: + description: "Run tests" + required: false + default: "true" + type: boolean + +permissions: + contents: read + +concurrency: + group: nextjs-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + stylelint-generated-css: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Fetch through merge-base + if: github.event_name == 'pull_request' + uses: fulcrumgenomics/fetch-through-merge-base@v1 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Build generated CSS + run: | + mkdir -p .ci-artifacts + npx tailwindcss -i app/globals.css -o .ci-artifacts/generated.css --minify + + - name: Run stylelint on generated CSS + run: | + npx stylelint .ci-artifacts/generated.css || { + echo "STYLE_LINT_FAILED: .ci-artifacts/generated.css を確認し、app/globals.css と Tailwind レイヤー定義から逆引きしてください。" + exit 1 + } + + build: + runs-on: ubuntu-latest + env: + RUN_TESTS: ${{ github.event.inputs.runTests || 'true' }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Fetch through merge-base + if: github.event_name == 'pull_request' + uses: fulcrumgenomics/fetch-through-merge-base@v1 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Test + run: npm run test diff --git a/.github/scaffold/nextjs/workflows/ci-uiux-storybook.yml b/.github/scaffold/nextjs/workflows/ci-uiux-storybook.yml new file mode 100644 index 0000000..80c2654 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/ci-uiux-storybook.yml @@ -0,0 +1,120 @@ +name: CI(UI/UX Storybookゲート) + +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + workflow_dispatch: + inputs: + runChromatic: + description: "Run Chromatic publish" + required: false + default: true + type: boolean + +permissions: + contents: read + +concurrency: + group: uiux-storybook-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + storybook-gate: + if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, '📕 UI/UX')) + runs-on: ubuntu-latest + env: + RUN_CHROMATIC: ${{ github.event_name == 'workflow_dispatch' && inputs.runChromatic || github.event_name == 'pull_request' && 'true' || 'false' }} + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Fetch through merge-base + if: github.event_name == 'pull_request' + uses: fulcrumgenomics/fetch-through-merge-base@v1 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Restore Playwright browsers cache + id: playwright-cache + uses: actions/cache@v5 + with: + path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} + key: playwright-${{ runner.os }}-uiux-storybook-browsers + restore-keys: | + playwright-${{ runner.os }}-uiux-storybook-browsers + + - name: Report Playwright browsers cache + run: | + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then + echo "Playwright cache hit" + else + echo "Playwright cache miss" + fi + + - name: Install Playwright Browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps + + - name: Build Storybook + run: npm run build-storybook + + - name: Test Storybook stories + run: | + set -e + python -m http.server 6006 --directory storybook-static --bind 127.0.0.1 & + STORYBOOK_PID=$! + trap "kill $STORYBOOK_PID" EXIT + MAX_RETRIES=10 + for i in $(seq 1 $MAX_RETRIES); do + if curl -fs http://127.0.0.1:6006 > /dev/null; then + break + fi + sleep 1 + done + if ! curl -fs http://127.0.0.1:6006 > /dev/null; then + echo "Storybook server did not become ready on port 6006 after ${MAX_RETRIES} attempts. Check for port conflicts or server startup issues." + exit 1 + fi + echo "Storybook server ready. Running Storybook tests..." + npx test-storybook --url http://127.0.0.1:6006 || { echo "Storybook tests failed."; exit 1; } + shell: bash + + - name: Publish to Chromatic + if: env.RUN_CHROMATIC == 'true' && env.CHROMATIC_PROJECT_TOKEN != '' + env: + CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + uses: chromaui/action@v15 + with: + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + exitOnceUploaded: true + + - name: Report Playwright cache status (post) + if: always() + run: | + echo "Playwright cache path: $PLAYWRIGHT_BROWSERS_PATH" + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then + echo "Playwright cache hit on restore" + else + echo "Playwright cache miss on restore" + fi + if [ -d "$PLAYWRIGHT_BROWSERS_PATH" ]; then + echo "Playwright cache size: $(du -sh "$PLAYWRIGHT_BROWSERS_PATH" | cut -f1)" + if [ "$(find "$PLAYWRIGHT_BROWSERS_PATH" -type f | head -n 1)" ]; then + echo "Playwright cache files detected" + else + echo "Playwright cache directory is empty" + fi + else + echo "Playwright cache directory missing" + fi diff --git a/.github/scaffold/nextjs/workflows/commitlint-pr.yml b/.github/scaffold/nextjs/workflows/commitlint-pr.yml new file mode 100644 index 0000000..aae77bd --- /dev/null +++ b/.github/scaffold/nextjs/workflows/commitlint-pr.yml @@ -0,0 +1,41 @@ +name: コミットメッセージlint(PR) + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: read + +concurrency: + group: commitlint-pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + commitlint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Commitlint + env: + FROM_SHA: ${{ github.event.pull_request.base.sha }} + TO_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + echo "Linting commits from ${FROM_SHA} to ${TO_SHA}" + npx commitlint --from "${FROM_SHA}" --to "${TO_SHA}" diff --git a/.github/scaffold/nextjs/workflows/copilot-setup-steps.yml b/.github/scaffold/nextjs/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000..cb02dc6 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/copilot-setup-steps.yml @@ -0,0 +1,37 @@ +name: "Copilot 環境準備" + +on: + workflow_dispatch: + # push: 西山がレビューで却下、文字化けしたらコメントアウト解除 + # pull_request: 西山がレビューで却下、文字化けしたらコメントアウト解除 + +permissions: + contents: read + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + steps: + - name: Cache fontconfig (for Copilot agent) + id: cache-fontconfig + uses: actions/cache@v5 + with: + path: ~/.cache/fontconfig + key: fontconfig-${{ runner.os }}-v1 + + - name: Echo cache status + run: | + if [ "${{ steps.cache-fontconfig.outputs.cache-hit }}" = "true" ]; then + echo "[INFO] fontconfig cache: HIT" + else + echo "[INFO] fontconfig cache: MISS" + fi + + - name: Install Japanese fonts for Copilot agent + run: | + sudo apt-get install -y --no-install-recommends \ + fonts-ipafont-gothic \ + fonts-ipafont-mincho \ + fonts-noto-cjk \ + fonts-noto-color-emoji + fc-cache -f \ No newline at end of file diff --git a/.github/scaffold/nextjs/workflows/deploy-gh-pages.yml b/.github/scaffold/nextjs/workflows/deploy-gh-pages.yml new file mode 100644 index 0000000..72eca72 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/deploy-gh-pages.yml @@ -0,0 +1,67 @@ +name: GitHub Pagesデプロイ(モックアプリ) + +on: + workflow_dispatch: + inputs: + mock_version: + description: "Deploy target mock version (e.g. v1)" + required: false + default: "v1" + +env: + MOCK_VERSION: ${{ github.event.inputs.mock_version || 'v1' }} + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + concurrency: + group: gh-pages-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: mock/${{ env.MOCK_VERSION }}/web/package-lock.json + + - name: Install dependencies + working-directory: mock/${{ env.MOCK_VERSION }}/web + run: npm ci --legacy-peer-deps + + - name: Build + working-directory: mock/${{ env.MOCK_VERSION }}/web + env: + VITE_BASE_PATH: /Agile-PMBOK-Assist/ + run: npm run build + + - name: Prepare gh-pages artifacts + working-directory: mock/${{ env.MOCK_VERSION }}/web + run: | + rm -rf dist-gh-pages + mkdir -p dist-gh-pages + if [ -f dist/client/index.html ]; then + cp -R dist/client/* dist-gh-pages/ + elif [ -f dist/index.html ]; then + cp -R dist/* dist-gh-pages/ + else + echo "index.html not found in dist/client or dist" + exit 1 + fi + if [ -f dist-gh-pages/index.html ] && [ ! -f dist-gh-pages/404.html ]; then + cp dist-gh-pages/index.html dist-gh-pages/404.html + fi + + - name: Deploy to gh-pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: mock/${{ env.MOCK_VERSION }}/web/dist-gh-pages + publish_branch: gh-pages diff --git a/.github/scaffold/nextjs/workflows/e2e-playwright.yml b/.github/scaffold/nextjs/workflows/e2e-playwright.yml new file mode 100644 index 0000000..86950a2 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/e2e-playwright.yml @@ -0,0 +1,86 @@ +name: E2E(Playwright ゲート) + +on: + pull_request: + types: [opened, synchronize, reopened, labeled] + paths-ignore: + - "mock/**" + - "**/*.md" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: e2e-playwright-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + playwright-e2e: + if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, '🔬 E2E')) + runs-on: ubuntu-latest + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 2 + + - name: Fetch through merge-base + if: github.event_name == 'pull_request' + uses: fulcrumgenomics/fetch-through-merge-base@v1 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Restore Playwright browsers cache + id: playwright-cache + uses: actions/cache@v5 + with: + path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} + key: playwright-${{ runner.os }}-e2e-chromium + restore-keys: | + playwright-${{ runner.os }}-e2e-chromium + + - name: Report Playwright browsers cache + run: | + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then + echo "Playwright cache hit" + else + echo "Playwright cache miss" + fi + + - name: Install Playwright Browsers (Chromium) + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install chromium + + - name: Test (E2E) + run: npm run test:e2e + + - name: Report Playwright cache status (post) + if: always() + run: | + echo "Playwright cache path: $PLAYWRIGHT_BROWSERS_PATH" + if [ "${{ steps.playwright-cache.outputs.cache-hit }}" = "true" ]; then + echo "Playwright cache hit on restore" + else + echo "Playwright cache miss on restore" + fi + if [ -d "$PLAYWRIGHT_BROWSERS_PATH" ]; then + echo "Playwright cache size: $(du -sh "$PLAYWRIGHT_BROWSERS_PATH" | cut -f1)" + if [ "$(find "$PLAYWRIGHT_BROWSERS_PATH" -type f | head -n 1)" ]; then + echo "Playwright cache files detected" + else + echo "Playwright cache directory is empty" + fi + else + echo "Playwright cache directory missing" + fi diff --git a/.github/scaffold/nextjs/workflows/infra-shell-audit.yml b/.github/scaffold/nextjs/workflows/infra-shell-audit.yml new file mode 100644 index 0000000..17009c1 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/infra-shell-audit.yml @@ -0,0 +1,65 @@ +name: インフラシェル監査 + +on: + pull_request: + paths: + - "infra/**" + push: + branches: [main] + paths: + - "infra/**" + +permissions: + contents: read + +concurrency: + group: infra-shell-audit-${{ github.ref }} + cancel-in-progress: true + +jobs: + infra-shell-audit: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Collect target scripts + shell: bash + run: | + set -euo pipefail + IFS=$'\n\t' + + mapfile -d '' -t files < <(find infra -type f -print0) + targets=() + for file in "${files[@]}"; do + if [[ "$file" == *.sh ]]; then + targets+=("$file") + elif [[ "$(head -n 1 "$file")" == "#!/usr/bin/env bash" ]]; then + targets+=("$file") + fi + done + + if ((${#targets[@]} == 0)); then + : > /tmp/infra-shell-targets.txt + else + printf '%s\n' "${targets[@]}" | sort -u > /tmp/infra-shell-targets.txt + fi + if [[ ! -s /tmp/infra-shell-targets.txt ]]; then + echo "No infra bash scripts found." + fi + + - name: Bash syntax check (bash -n) + shell: bash + run: | + set -euo pipefail + while IFS= read -r file; do + bash -n "$file" + done < /tmp/infra-shell-targets.txt + + - name: ShellCheck (bash) + shell: bash + run: | + set -euo pipefail + while IFS= read -r file; do + shellcheck -s bash "$file" + done < /tmp/infra-shell-targets.txt diff --git a/.github/scaffold/nextjs/workflows/markdownlint.yml b/.github/scaffold/nextjs/workflows/markdownlint.yml new file mode 100644 index 0000000..20b96a2 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/markdownlint.yml @@ -0,0 +1,83 @@ +name: Markdownチェック(reviewdog) + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "docs/**/*.md" + - ".github/**/*.md" + +permissions: + contents: read + +concurrency: + group: markdownlint-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + markdownlint-pr: + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Install tools + run: npm install -g markdownlint-cli@0.48.0 + + - name: Log goコマンド確認開始 + run: echo "goコマンドの存在を確認します" >&2 + + - name: Check Go availability + run: | + if ! command -v go >/dev/null 2>&1; then + echo "goコマンドが見つからないためジョブを失敗扱いにします" >&2 + exit 1 + fi + + - name: Install reviewdog + run: | + go install github.com/reviewdog/reviewdog/cmd/reviewdog@v0.21.0 + echo "$HOME/go/bin" >> "$GITHUB_PATH" + + - name: Log markdownlint開始 + run: echo "markdownlintとreviewdogを実行します" >&2 + + - name: Markdown lint + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + # 複数コマンドをパイプで繋いでいないため pipefail は省略 + exit_code=0 + markdownlint "docs/**/*.md" ".github/**/*.md" > markdownlint.txt || exit_code=$? + reviewdog_exit=0 + reviewdog < markdownlint.txt \ + -efm="%f:%l:%c %m" \ + -efm="%f:%l %m" \ + -name="Markdownチェック" \ + -reporter="github-pr-review" \ + -level=warning || reviewdog_exit=$? + # markdownlint: 1=lint違反, 2以上=実行エラー + # reviewdog: 0=成功(PRコメント/チェックの投稿が成功) + if [ "$reviewdog_exit" -ne 0 ]; then + echo "reviewdogが失敗しました: exit code ${reviewdog_exit}" >&2 + exit "$reviewdog_exit" + fi + # exit_code=1 は reviewdog で通知済みのため成功扱い + if [ "$exit_code" -gt 1 ]; then + echo "markdownlintが失敗しました: exit code ${exit_code}" >&2 + exit "$exit_code" + fi + + - name: Log markdownlint完了 + run: echo "markdownlintとreviewdogの処理が完了しました" >&2 diff --git a/.github/scaffold/nextjs/workflows/mermaid-lint.yml b/.github/scaffold/nextjs/workflows/mermaid-lint.yml new file mode 100644 index 0000000..6b041cc --- /dev/null +++ b/.github/scaffold/nextjs/workflows/mermaid-lint.yml @@ -0,0 +1,221 @@ +name: Mermaid図チェック(reviewdog) + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "docs/**/*.md" + - ".github/**/*.md" + +permissions: + contents: read + +concurrency: + group: mermaid-lint-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + mermaid: + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Log Mermaid CLIインストール開始 + run: echo "Mermaid CLIをインストールします" >&2 + + - name: Install Mermaid CLI + run: | + set -e + npm install -g @mermaid-js/mermaid-cli@11.12.0 + + - name: Log Mermaid CLIインストール完了 + run: echo "Mermaid CLIのインストールが完了しました" >&2 + + - name: Log mmdcコマンド確認開始 + run: echo "mmdcコマンドの存在を確認します" >&2 + + - name: Check mmdc availability + run: | + if ! command -v mmdc >/dev/null 2>&1; then + echo "mmdcコマンドが見つからないためジョブを失敗扱いにします" >&2 + exit 1 + fi + + - name: Log goコマンド確認開始 + run: echo "goコマンドの存在を確認します" >&2 + + - name: Check Go availability + run: | + if ! command -v go >/dev/null 2>&1; then + echo "goコマンドが見つからないためジョブを失敗扱いにします" >&2 + exit 1 + fi + + - name: Install reviewdog + run: | + go install github.com/reviewdog/reviewdog/cmd/reviewdog@v0.21.0 + echo "$HOME/go/bin" >> "$GITHUB_PATH" + + - name: Log Mermaidブロック抽出開始 + run: echo "Mermaidブロック抽出を開始します" >&2 + + - name: Extract Mermaid blocks + run: | + mkdir -p tmp + node <<'EOF' + const fs = require('fs') + const path = require('path') + + // Mermaid抽出ファイルと元Markdownの対応を「抽出ファイル:元ファイル:開始行」形式で記録 + // 例: tmp/diagram-1.mmd:docs/example.md:42 + const mapPath = path.join('tmp', 'map.txt') + fs.writeFileSync(mapPath, '') + + // Mermaidブロックの件数 + let index = 0 + + // 指定ディレクトリ以下のMarkdownを探索 + function walk(dir) { + if (!fs.existsSync(dir)) return + fs.readdirSync(dir).forEach((entry) => { + const full = path.join(dir, entry) + const stat = fs.statSync(full) + if (stat.isDirectory()) { + walk(full) + return + } + if (full.endsWith('.md')) { + processFile(full) + } + }) + } + + function processFile(file) { + const lines = fs.readFileSync(file, 'utf8').split('\n') + let inside = false + let buffer = [] + let startLine = 0 + + lines.forEach((line, idx) => { + const trimmed = line.trim() + // Mermaidブロックの開始 + if (!inside && trimmed.startsWith('```mermaid')) { + inside = true + buffer = [] + startLine = idx + 1 + return + } + + // Mermaidブロックの終了 + if (inside && trimmed.startsWith('```')) { + inside = false + const name = `tmp/diagram-${++index}.mmd` + fs.writeFileSync(name, buffer.join('\n')) + fs.appendFileSync(mapPath, `${name}:${file}:${startLine}\n`) + return + } + + if (inside) buffer.push(line) + }) + + // ファイル末尾まで到達しても終了フェンスが無い場合(閉じ忘れ)もブロックを検証対象に含める + if (inside) { + const name = `tmp/diagram-${++index}.mmd` + fs.writeFileSync(name, buffer.join('\n')) + fs.appendFileSync(mapPath, `${name}:${file}:${startLine}\n`) + } + } + + walk('docs') + walk('.github') + console.error(`抽出したMermaid図: ${index}件`) + EOF + + - name: Log Mermaidブロック抽出完了 + run: echo "Mermaidブロック抽出が完了しました" >&2 + + - name: Log Mermaid図検証開始 + run: echo "Mermaid図の検証を開始します" >&2 + + - name: Validate Mermaid diagrams + run: | + : > tmp/result.txt + MAX_ERROR_LENGTH=500 + # Mermaid CLIの実行環境起因エラー(Puppeteer/ブラウザ起動失敗等)を判定するための正規表現パターン + # 各要素は正規表現として評価するため、必要に応じてメタ文字を使用/エスケープする + # 出力メッセージの大文字小文字揺れを吸収するため、grepは -i を使用する + MMD_CLI_EXEC_ERROR_PATTERNS=( + # Puppeteer関連のエラー文言(例: "Error: Puppeteer failed to connect") + 'error.*puppeteer' + # 実行ファイル未検出(例: "Executable doesn't exist at /path/to/chrome") + "executable doesn't exist" + # browser executable.*exist は実行ファイル探索時の存在エラー検知を意図(例: "browser executable doesn't exist") + 'browser executable.*exist' + # 起動失敗の共通文言(例: "Failed to launch the browser process!") + 'failed to launch' + # browsertype.launch のドットはリテラルとして扱う(例: "browserType.launch: Executable doesn't exist ...") + 'browsertype\.launch' + ) + # 配列要素を | で連結して正規表現文字列を生成(例: error.*puppeteer|...) + MMD_CLI_EXEC_ERROR_REGEX=$(IFS='|'; printf '%s' "${MMD_CLI_EXEC_ERROR_PATTERNS[*]}") + cat <<'JSON' > tmp/puppeteer-config.json + { + "args": ["--no-sandbox", "--disable-setuid-sandbox"] + } + JSON + if [ -s tmp/map.txt ]; then + mmdc_exec_error=0 + mmdc_exec_summary="" + while IFS=":" read -r mmd src line; do + if [ -z "${mmd}" ]; then + continue + fi + if ! output=$(mmdc -p tmp/puppeteer-config.json -i "${mmd}" -o "${mmd}.svg" 2>&1); then + summary=$(printf "%s" "${output}" | tr '\n' ' ' | cut -c1-${MAX_ERROR_LENGTH}) + if printf "%s" "${output}" | grep -qiE "${MMD_CLI_EXEC_ERROR_REGEX}"; then + mmdc_exec_error=1 + mmdc_exec_summary="${summary}" + echo "mmdcの実行に失敗しました: ${summary}" >&2 + # 実行環境エラーは全図に影響するため、以降の検証を中断してジョブを失敗扱いにする + break + fi + echo "${src}:${line}: Mermaid図の構文検証に失敗しました: ${summary}" >> tmp/result.txt + fi + done < tmp/map.txt + if [ "${mmdc_exec_error}" -ne 0 ]; then + echo "Mermaid CLIの実行エラーのためジョブを失敗扱いにします: ${mmdc_exec_summary}" >&2 + exit 1 + fi + else + echo "Mermaidブロックが見つからなかったため検証をスキップします" >&2 + fi + + - name: Log Mermaid図検証完了 + run: echo "Mermaid図の検証が完了しました" >&2 + + - name: Log reviewdog通知開始 + run: echo "reviewdogで結果を通知します" >&2 + + - name: Report via reviewdog + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + reviewdog < tmp/result.txt \ + -efm="%f:%l: %m" \ + -name="Mermaid図チェック" \ + -reporter=github-pr-review \ + -level=error + + - name: Log reviewdog通知完了 + run: echo "reviewdogの通知処理が完了しました" >&2 diff --git a/.github/scaffold/nextjs/workflows/mock-ci.yml b/.github/scaffold/nextjs/workflows/mock-ci.yml new file mode 100644 index 0000000..5fe4525 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/mock-ci.yml @@ -0,0 +1,66 @@ +name: モックCI(React) + +on: + pull_request: + paths: + - "mock/**" + push: + branches: [main] + paths: + - "mock/**" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: mock-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + mock-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install root dependencies + run: npm ci + + - name: Lint mock (mock-specific config) + run: npx eslint --config eslint.config.mock.mjs mock/ + + mock-web-build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: mock/v1/web/package-lock.json + + - name: Verify lockfile + run: test -f mock/v1/web/package-lock.json + + - name: Install dependencies + working-directory: mock/v1/web + run: npm ci --legacy-peer-deps + + - name: Typecheck (mock/v1/web/tsconfig.json) + working-directory: mock/v1/web + run: npm run typecheck + + - name: Build + working-directory: mock/v1/web + run: npm run build diff --git a/.github/scaffold/nextjs/workflows/release-date.yml b/.github/scaffold/nextjs/workflows/release-date.yml new file mode 100644 index 0000000..255eaa8 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/release-date.yml @@ -0,0 +1,297 @@ +name: リリース(日付ベース) + +on: + workflow_dispatch: + inputs: + release_date: + description: "Release date (JST, YYYY.MM.DD)" + required: true + type: string + enforce_commitlint: + description: "Enforce Conventional Commits with commitlint" + required: true + type: boolean + default: true + enforce_lint: + description: "Enforce lint (fail on lint errors). Set false to skip lint gate and allow release to proceed." + required: false + type: boolean + default: true + enforce_audit: + description: "Enforce npm audit (fail on vulnerabilities). Set false to skip audit gate and allow release to proceed." + required: false + type: boolean + default: true + +permissions: + contents: write + +concurrency: + group: release-date-${{ github.event.inputs.release_date }} + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + env: + RELEASE_DATE: ${{ github.event.inputs.release_date }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Verify workflow commit + run: | + set -euo pipefail + head_sha=$(git rev-parse HEAD) + if [[ "${head_sha}" != "${GITHUB_SHA}" ]]; then + echo "Workflow must run from HEAD. Expected ${GITHUB_SHA}, got ${head_sha}." >&2 + exit 1 + fi + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Validate release date + run: | + set -euo pipefail + if [[ -z "${RELEASE_DATE}" ]]; then + echo "release_date input is required." >&2 + exit 1 + fi + if [[ ! "${RELEASE_DATE}" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$ ]]; then + echo "release_date must be in YYYY.MM.DD format." >&2 + exit 1 + fi + + - name: Install dependencies + run: npm ci + + - name: Determine release tag + id: release_meta + run: | + set -euo pipefail + git fetch --tags --force + + base_tag=$(git tag -l "${RELEASE_DATE}") + mapfile -t date_tags < <(git tag -l "${RELEASE_DATE}*") + + if [[ -z "${base_tag}" ]]; then + next_tag="${RELEASE_DATE}" + else + regex_date=${RELEASE_DATE//./\\.} + suffixes=() + for tag in "${date_tags[@]}"; do + if [[ "${tag}" =~ ^${regex_date}-([0-9]+)$ ]]; then + suffixes+=("${BASH_REMATCH[1]}") + fi + done + + next_suffix=2 + while true; do + found=0 + for suffix in "${suffixes[@]}"; do + if [[ "${suffix}" == "${next_suffix}" ]]; then + found=1 + break + fi + done + + if [[ "${found}" -eq 0 ]]; then + break + fi + next_suffix=$((next_suffix + 1)) + done + next_tag="${RELEASE_DATE}-${next_suffix}" + fi + + last_tag=$(git tag --sort=-creatordate | head -n 1 || true) + if [[ -z "${last_tag}" ]]; then + range_from=$(git rev-list --max-parents=0 HEAD | tail -n 1) + else + range_from="${last_tag}" + fi + + echo "next_tag=${next_tag}" >> "${GITHUB_OUTPUT}" + echo "last_tag=${last_tag}" >> "${GITHUB_OUTPUT}" + echo "range_from=${range_from}" >> "${GITHUB_OUTPUT}" + echo "release_tag=${next_tag}" + if [[ -n "${last_tag}" ]]; then + echo "previous_tag=${last_tag}" + else + echo "previous_tag=none" + fi + + - name: Verify tag and release do not exist + run: | + set -euo pipefail + tag="${{ steps.release_meta.outputs.next_tag }}" + if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then + echo "Tag ${tag} already exists." >&2 + exit 1 + fi + if gh release view "${tag}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then + echo "Release ${tag} already exists." >&2 + exit 1 + fi + + - name: Commitlint + if: ${{ github.event.inputs.enforce_commitlint == 'true' }} + run: | + set -euo pipefail + npx commitlint --from "${{ steps.release_meta.outputs.range_from }}" --to HEAD + + - name: Lint + if: ${{ github.event.inputs.enforce_lint == 'true' }} + run: npm run lint + + - name: Lint (warn-only) + if: ${{ github.event.inputs.enforce_lint != 'true' }} + continue-on-error: true + run: npm run lint + + - name: Security audit + if: ${{ github.event.inputs.enforce_audit == 'true' }} + run: npm audit + + - name: Security audit (warn-only) + if: ${{ github.event.inputs.enforce_audit != 'true' }} + continue-on-error: true + run: npm audit + + - name: Generate release notes + if: ${{ github.event.inputs.enforce_commitlint == 'true' }} + run: | + set -euo pipefail + range_from="${{ steps.release_meta.outputs.range_from }}" + npx conventional-changelog -p conventionalcommits -r 0 --tag-prefix '' -o release-notes.md -- "${range_from}..HEAD" + - name: Generate release notes (fallback) + if: ${{ github.event.inputs.enforce_commitlint != 'true' }} + run: | + set -euo pipefail + range_from="${{ steps.release_meta.outputs.range_from }}" + git log --pretty=format:"- %s (%h)" "${range_from}..HEAD" > release-notes.md + if [[ ! -s release-notes.md ]]; then + echo "- 変更履歴なし" > release-notes.md + fi + + - name: Build standalone + run: npm run build + + - name: Create bundle + run: | + set -euo pipefail + test -f .next/standalone/server.js + rm -rf bundle + mkdir -p bundle + cp -R .next/standalone/* bundle/ + rm -rf bundle/.next + cp -R .next bundle/.next + rm -rf bundle/.next/cache + cp -R public bundle/public + cp package.json bundle/package.json + test -f bundle/server.js + tar -czf next-bundle.tgz -C bundle . + + - name: Debug bundle and archive listing + shell: bash + run: | + set -euo pipefail + echo "=== bundle tree (top) ===" + ls -la bundle || true + echo "=== bundle find server.js ===" + find bundle -maxdepth 6 -type f -name 'server.js' -print -exec ls -la {} \; || true + echo "=== bundle tree (deeper) ===" + find bundle -maxdepth 4 -print | sed 's#^\./##' | sort | sed -n '1,400p' + echo "=== tar listing (head) ===" + tar -tzf next-bundle.tgz | sed -n '1,400p' + echo "=== tar grep server.js ===" + tar -tzf next-bundle.tgz | grep -nE 'server\.js$' || true + echo "=== tar grep package.json ===" + tar -tzf next-bundle.tgz | grep -nE 'package\.json$' || true + + - name: Verify bundle archive + run: | + set -euo pipefail + tar -tzf next-bundle.tgz > bundle-listing.txt + + require_entry() { + local pattern="$1" + local label="$2" + if ! grep -qE "${pattern}" bundle-listing.txt; then + echo "Bundle archive missing ${label}." >&2 + exit 1 + fi + } + + require_entry '(^|\./)server\.js$' "server.js" + require_entry '(^|\./)package\.json$' "package.json" + require_entry '(^|\./)public/' "public/" + require_entry '(^|\./)\.next/static/' ".next/static/" + require_entry '(^|\./)\.next/BUILD_ID$' ".next/BUILD_ID" + require_entry '(^|\./)\.next/required-server-files\.json$' ".next/required-server-files.json" + require_entry '(^|\./)\.next/routes-manifest\.json$' ".next/routes-manifest.json" + require_entry '(^|\./)\.next/prerender-manifest\.json$' ".next/prerender-manifest.json" + require_entry '(^|\./)\.next/build-manifest\.json$' ".next/build-manifest.json" + require_entry '(^|\./)\.next/server/pages-manifest\.json$' ".next/server/pages-manifest.json" + require_entry '(^|\./)\.next/server/app-paths-manifest\.json$' ".next/server/app-paths-manifest.json" + require_entry '(^|\./)\.next/server/app/' ".next/server/app/" + + - name: Create release and upload asset + run: | + set -euo pipefail + tag="${{ steps.release_meta.outputs.next_tag }}" + + retry_command() { + local description="$1" + shift + local attempt=1 + local output="" + local status=0 + + while true; do + set +e + output=$("$@" 2>&1) + status=$? + set -e + + if [[ ${status} -eq 0 ]]; then + echo "${output}" + return 0 + fi + + if echo "${output}" | grep -E "HTTP 4[0-9]{2}|status 4[0-9]{2}" >/dev/null; then + # Treat HTTP 429 and rate limit / abuse-detection HTTP 403 as retryable. + if echo "${output}" | grep -E "HTTP 429|status 429" >/dev/null; then + : + elif echo "${output}" | grep -E "HTTP 403|status 403" >/dev/null && \ + echo "${output}" | grep -Ei "rate limit|abuse" >/dev/null; then + : + else + echo "${output}" + echo "${description} failed with 4xx; no retry." >&2 + return ${status} + fi + fi + + if [[ ${attempt} -ge 3 ]]; then + echo "${output}" + echo "${description} failed after ${attempt} attempts." >&2 + return ${status} + fi + + attempt=$((attempt + 1)) + echo "${description} failed; retrying (${attempt}/3)..." >&2 + sleep 5 + done + } + + retry_command "Release create" gh release create "${tag}" --title "${tag}" --notes-file release-notes.md --target "${GITHUB_SHA}" + retry_command "Release upload" gh release upload "${tag}" next-bundle.tgz diff --git a/.github/scaffold/nextjs/workflows/textlint.yml b/.github/scaffold/nextjs/workflows/textlint.yml new file mode 100644 index 0000000..6592ee1 --- /dev/null +++ b/.github/scaffold/nextjs/workflows/textlint.yml @@ -0,0 +1,40 @@ +name: ドキュメントlint(textlint/PR) + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "docs/**/*.md" + - ".github/**/*.md" + - "README.md" + - ".textlintrc" + - "prh.yml" + - ".textlintignore" + +permissions: + contents: read + +concurrency: + group: textlint-pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + textlint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Textlint + run: npm run lint:text diff --git a/.github/workflows/create-issue.yaml b/.github/workflows/create-issue.yaml index f3418d1..7b394ac 100644 --- a/.github/workflows/create-issue.yaml +++ b/.github/workflows/create-issue.yaml @@ -15,7 +15,7 @@ jobs: # ストラテジー設定: マトリックス戦略を使用して、異なるファイル名でジョブを実行 strategy: matrix: - filename: ["workflow-cleanup"] # 使用するファイル名を指定 + filename: ["workflow-cleanup", "dependabot-yml-enable"] # 使用するファイル名を指定 # 実行環境: ubuntu-latest runs-on: ubuntu-latest steps: diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 507aacd..20b96a2 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -6,10 +6,6 @@ on: paths: - "docs/**/*.md" - ".github/**/*.md" - push: - paths: - - "docs/**/*.md" - - ".github/**/*.md" permissions: contents: read @@ -20,7 +16,6 @@ concurrency: jobs: markdownlint-pr: - if: github.event_name == 'pull_request' runs-on: ubuntu-latest permissions: @@ -67,7 +62,8 @@ jobs: markdownlint "docs/**/*.md" ".github/**/*.md" > markdownlint.txt || exit_code=$? reviewdog_exit=0 reviewdog < markdownlint.txt \ - -f=markdownlint \ + -efm="%f:%l:%c %m" \ + -efm="%f:%l %m" \ -name="Markdownチェック" \ -reporter="github-pr-review" \ -level=warning || reviewdog_exit=$? @@ -85,70 +81,3 @@ jobs: - name: Log markdownlint完了 run: echo "markdownlintとreviewdogの処理が完了しました" >&2 - - markdownlint-push: - if: github.event_name == 'push' - runs-on: ubuntu-latest - - permissions: - contents: read - checks: write - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: "24" - - - name: Install tools - run: npm install -g markdownlint-cli@0.48.0 - - - name: Log goコマンド確認開始 - run: echo "goコマンドの存在を確認します" >&2 - - - name: Check Go availability - run: | - if ! command -v go >/dev/null 2>&1; then - echo "goコマンドが見つからないためジョブを失敗扱いにします" >&2 - exit 1 - fi - - - name: Install reviewdog - run: | - go install github.com/reviewdog/reviewdog/cmd/reviewdog@v0.21.0 - echo "$HOME/go/bin" >> "$GITHUB_PATH" - - - name: Log markdownlint開始 - run: echo "markdownlintとreviewdogを実行します" >&2 - - - name: Markdown lint - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eu - # 複数コマンドをパイプで繋いでいないため pipefail は省略 - exit_code=0 - markdownlint "docs/**/*.md" ".github/**/*.md" > markdownlint.txt || exit_code=$? - reviewdog_exit=0 - reviewdog < markdownlint.txt \ - -f=markdownlint \ - -name="Markdownチェック" \ - -reporter="github-check" \ - -level=warning || reviewdog_exit=$? - # markdownlint: 1=lint違反, 2以上=実行エラー - # reviewdog: 0=成功(PRコメント/チェックの投稿が成功) - if [ "$reviewdog_exit" -ne 0 ]; then - echo "reviewdogが失敗しました: exit code ${reviewdog_exit}" >&2 - exit "$reviewdog_exit" - fi - # exit_code=1 は reviewdog で通知済みのため成功扱い - if [ "$exit_code" -gt 1 ]; then - echo "markdownlintが失敗しました: exit code ${exit_code}" >&2 - exit "$exit_code" - fi - - - name: Log markdownlint完了 - run: echo "markdownlintとreviewdogの処理が完了しました" >&2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7e472b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/docs/draft/*.md +/docs/draft/**/*.md +!/docs/draft/readme.md + diff --git a/.markdownlint.json b/.markdownlint.json index 26593a2..e906ab1 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,4 +1,19 @@ { + "MD004": false, + "MD009": false, + "MD010": false, + "MD012": false, + "MD022": false, + "MD025": false, + "MD026": false, + "MD031": false, + "MD032": false, + "MD034": false, + "MD040": false, + "MD041": false, + "MD047": false, + "MD058": false, + "MD060": false, "MD013": false, "MD033": false } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index dccf401..15782f0 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -11,5 +11,7 @@ "GitHub.codespaces", "GitHub.copilot", "GitHub.copilot-chat", + "openai.chatgpt", + "anthropic.claude-code", ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 98e39a8..45ab377 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,6 @@ // "editor.formatOnSave": true // }, "editor.tabSize": 2, + "compareFolders.excludeFilter": [ + ], } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d304b78 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Yoshio Nishiyama + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.