Skip to content

Research & Design Decisions — candidate-detail


Purpose: candidate-detail spec の design 書き直し(data-model の 3 系統契約 + voting の購入導線への適合)で行った調査・統合判断・トレードオフを記録する。

Summary

  • Feature: candidate-detail
  • Discovery Scope: Extension(既存の候補者詳細画面を、3 系統データアーキテクチャと最新の voting/candidate-ranking 契約に合わせて再構築)
  • Key Findings:
    • 旧 design の candidateService.getAllWithRank() / voterService.getTopByCandidateId() / DB 由来 votingPeriodService / /api/images/[id] がすべて廃止対象であり、4 つの参照経路を新契約に置き換える必要がある。
    • 順位算出は candidate-ranking spec が所有する features/candidates/utils/rankCandidates を再利用し、本 spec ではそこから target ID を抽出する一手間のみを担う。
    • 投票導線は voting spec 所有の <PurchaseFlowContainer> / useVoteFlow / useVotingPeriodStatus をそのまま借用し、本 spec で再実装しない。
    • 動的メタデータ(OGP / title)は要件には存在しないが、SEO 強化の選択肢として generateMetadata で扱う(non-functional 要件側に追記候補)。

Research Log

Topic 1: 旧設計のデータアクセス経路の総入れ替え

  • Context: 旧 design は Prisma 単独前提で candidateService.getAllWithRank() を呼び、画像は /api/images/[id] 経由で配信、投票期間は DB 由来。新 data-model 契約ではすべて経路が変わる。
  • Sources Consulted: 旧 candidate-detail/design.mddata-model/design.mdvoting/design.mdcandidate-ranking/design.md
  • Findings:
    • 候補者単体取得は getCandidate(id) で 404 判定可能、ただし順位算出には全候補者の voteCount が必要なので getCandidates() + getVoteCountsByCandidateIds() も並走する。
    • 応援者ランキングは getTopVotersByCandidateId(candidateId, limit=5) を新設(voteAggregationService 側で Purchase.status='SUCCEEDED' JOIN + nickname GROUP BY)。
    • 画像 URL は microCMS の CDN URL を next/image に直渡し可能。/api/images/[id] プロキシは不要。
    • 投票期間は getVotingPeriod()(Vercel ENV 由来)を Server で呼び、Client へは period オブジェクトを渡す。
  • Implications:
    • File Structure Plan から /api/images/[id] ルートを完全削除。
    • data-model spec 側に getTopVotersByCandidateId の追加が必要(対応する Boundary Commitments を data-model に伝播)。
    • 404 判定は単体 getCandidate の null 戻り値 + ランク済み配列の find 失敗の二段で行う(microCMS の draft 状態などの取りこぼし対策)。

Topic 2: 順位算出ユーティリティの再利用

  • Context: candidate-ranking spec で features/candidates/utils/rankCandidates を純関数化することにした。本 spec でも同じロジックが必要(順位は全候補者中の順位)。
  • Sources Consulted: candidate-ranking/design.mdcandidate-ranking/research.md
  • Findings:
    • 関数シグネチャ rankCandidates(items: { id, voteCount, ... }[]) => Ranked[] は本 spec で必要な「voteCount 降順 + ID 昇順タイブレーク」と完全に一致。
    • 表示順は呼び出し側の責務という方針(ranking の design 決定事項)に従い、本 spec では rank 付き配列から target ID を find するだけ。
  • Implications:
    • 本 spec の Allowed Dependencies に features/candidates/utils/rankCandidates を明示し、Out of Boundary に「rank 算出ルールの定義」を入れる。
    • candidate-ranking 側の関数変更があれば本 spec の Revalidation Trigger になる(両 spec で同じ関数を共有する宣言が必要)。

Topic 3: 順位ハイライト(useOptimistic 後の rank 不変)

  • Context: 要件 Feature 3 で「投票後も rank は不変」と明記。useOptimistic で voteCount を加算しても rank を再計算してはいけない。
  • Sources Consulted: 要件 Feature 3、home/design.md の楽観更新フック設計
  • Findings:
    • reducer 内で state.voteCount += delta のみ更新し、rank には触れない実装で要件を満たす。
    • home / candidate-detail で似た振る舞いをするため、共通化の余地はあるが、画面ごとに状態形が微妙に違う(home は配列、detail は単体)ため一旦別フックでよい。
  • Implications:
    • useOptimistic reducer のロジックを設計書に明示し、テストで rank 不変を検証する旨を Testing Strategy に記載。

Topic 4: 動的メタデータ(OGP / title)

  • Context: 旧 design に generateMetadata の記述があったが、要件には明示されていない。SEO 強化として残すか削除するかの判断が必要。
  • Sources Consulted: 旧 design、Next.js 15 generateMetadata ドキュメント
  • Findings:
    • generateMetadata は Server Component と同じデータ取得経路(getCandidate(id))を再利用でき、追加コストは microCMS 1 リクエストのみ。
    • 候補者名入り title + 画像 OGP は SNS シェアでの誘導力に直結する。
  • Implications:
    • 動的メタデータを non-functional 要件として design に明記。要件側に「SEO/OGP」項目を追加する余地は今後の要件改訂時に検討。
    • メタデータ用の取得は Next.js が page 描画前に自動で呼ぶため、page.tsx と二重取得になるが、unstable_cache or Request Memoization(Next.js 15)で抑える方針。

Architecture Pattern Evaluation

OptionDescriptionStrengthsRisks / Limitations
Server で 3 系統合成 + Client は presentation + 投票借用採用責務分離が鮮明、voting/candidate-ranking を再利用rank 付与のために全候補者の voteCount を取得する必要あり
Server で対象 1 件 + voteCount 1 件のみ取得軽量取得Neon クエリが軽い順位算出ができない(要件 Feature 3 違反)
Client で順位算出全候補者を Client へ送付Server レスポンスを 1 件に絞れるペイロード増大、初期描画に不利

Design Decisions

Decision: 404 判定を二段(単体 getCandidate + ランク済み配列 find)で行う

  • Context: microCMS の draft 状態や ID 不正で getCandidate が null を返すことがある。一方で getCandidates() のキャッシュタイミングずれで「全候補者には含まれていないが単体取得は成功する」逆ケースも理論上ありうる。
  • Alternatives Considered:
    1. getCandidate(id) 単体で 404 判定。
    2. getCandidates() + find で 404 判定。
    3. 両方で判定し、どちらかが失敗したら notFound()
  • Selected Approach: 3。安全側に倒す。
  • Rationale: microCMS のキャッシュ整合性は完全保証されないため、二段判定でユーザーに「壊れた詳細画面」を見せないことを優先。
  • Trade-offs: 微小な実装コスト増、判定の冗長性。ただし可読性は高い。
  • Follow-up: unstable_cache の TTL を candidate / ranking で揃え、二段判定の発火頻度を抑える。

Decision: 応援者ランキングを voteAggregationService.getTopVotersByCandidateId に集約

  • Context: 旧設計の voterService.getTopByCandidateId() は data-model 契約に存在せず、新規定義が必要。
  • Alternatives Considered:
    1. Page.tsx 内で Prisma 直接叩き。
    2. features/candidates/services/voteAggregationService に追加。
  • Selected Approach: 2。data-model の契約として明記し、他 spec からも再利用可能にする。
  • Rationale: Page.tsx に Prisma を持ち込むと steering の services 層責務に反する。集約クエリを 1 箇所に集めることで、Purchase.status='SUCCEEDED' JOIN ルールを単一の場所で守れる。
  • Trade-offs: data-model spec への変更が伴うため、本 spec の確定前に data-model 側で当該関数の Boundary Commitments を追記する必要がある。
  • Follow-up: data-model design に getTopVotersByCandidateId(candidateId: string, limit?: number) を追記(本 spec design の Out of Boundary で参照済み)。

Decision: 動的メタデータを採用し、Server Component と取得経路を共有

  • Context: generateMetadata を別経路で実装すると保守性が落ちる。
  • Alternatives Considered:
    1. 静的メタデータのみ(全候補者で同じ title)。
    2. generateMetadatagetCandidate(id) を再利用。
  • Selected Approach: 2。
  • Rationale: SEO/シェア導線の改善効果が大きい。Next.js 15 の Request Memoization で実質 1 回の取得に最適化される。
  • Trade-offs: メタデータ取得失敗時のフォールバック(候補者未存在で Not Found タイトル)を明記する必要あり。
  • Follow-up: generateMetadata 失敗時の挙動を Testing Strategy に追加。

Risks & Mitigations

  • microCMS の整合性ずれ: 単体取得と一覧取得の結果が一時的に食い違うと、両方で 404 判定する設計で最終的に notFound() に倒す。
  • 応援者ランキング集計の重さ: ニックネーム単位 GROUP BY は Purchase JOIN を伴うため、(candidateId, status) 複合 INDEX を data-model 側で確保。
  • 画像 LCP: 最初の画像のみ priority を付与し、それ以外は遅延読み込み(ui-design.md 委譲)。
  • メタデータ取得失敗: try/catch で fallback Not Found を返す。Stripe / DB 障害には依存しないので影響は限定的。
  • 楽観更新の rank 巻き込み: reducer テストで rank 不変を強制(Testing Strategy 必須項目)。

References

  • .kiro/specs/candidate-detail/requirements.md — 機能要件
  • .kiro/specs/data-model/design.mdCandidate / voteAggregationService / nicknameService
  • .kiro/specs/voting/design.md<PurchaseFlowContainer> / useVoteFlow
  • .kiro/specs/candidate-ranking/design.mdrankCandidates 純関数
  • .kiro/steering/structure.md — features / components / services 責務分離
  • Next.js generateMetadata

Gap Analysis — candidate-detail (2026-05-17)


Purpose: 既に承認済みの requirements / design / tasks と、現行コードベース(2026-05 リセット後の最小スケルトン)との間の実装ギャップを洗い出し、/kiro-impl candidate-detail 起動時の戦略判断を補助する情報を提供する。判断はこの分析の利用者(人間 + 後続 skill)に委ねる。

Summary

  • 対象 spec: candidate-detail(phase: tasks-generated、requirements / design / tasks すべて承認済み)
  • 既存実装の状態: ほぼ greenfieldapp/page.tsx / app/layout.tsx / app/providers.tsx / components/ui/*(shadcn 9 種) / design-tokens/* / lib/utils.ts のみが存在し、app/candidate/[id]/**, features/**, components/public/**, prisma/**すべて未作成
  • 依存 spec の前提: data-model, candidate-ranking, voting の 3 つの spec が本 spec の Allowed Dependencies(getCandidate / getCandidates / getVoteCountsByCandidateIds / getTopVotersByCandidateId / rankCandidates / RankedCandidate / useVoteFlow / useVotingPeriodStatus / <PurchaseFlowContainer> / lib/voting-period.getVotingPeriod)を所有する。すべて未実装。
  • 推奨アプローチ: Option B(新規ファイルのみ)が design.md の File Structure Plan と完全一致する。前提 spec の完了を待ってから本 spec の Foundation → Core → Integration → 検証の順で /kiro-impl する正攻法が最も低リスク。
  • 重大リスク: 依存パッケージ(microcms-js-sdk / @stripe/react-stripe-js / @upstash/ratelimit / @upstash/redis)未導入、lucide-react のバージョンが steering 想定(0.487.0+)と乖離(^1.12.0)、shadcn ^4.6.0 の正体不明エントリ等、package.json 側の不整合が複数。前提 spec の impl 段階で発覚するためここで指摘するに留める。

1. Current State Investigation

1.1 リポジトリ構造の実測

/home/mikamix/works/miss-world-japan/
├── app/
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx              # Cormorant Garamond + Jost + Providers
│   ├── next-env.d.ts
│   ├── page.tsx                # 「実装は .kiro/specs に基づいて再構築中」プレースホルダ
│   └── providers.tsx           # 中身空(children pass-through)
├── components/
│   └── ui/                     # shadcn: button/card/dialog/input/label/sonner/table/textarea
├── design-tokens/              # colors.ts / tokens.css / typography.ts
├── lib/
│   └── utils.ts                # cn() のみ想定
├── docker-compose.yml          # postgres:17 用(未確認だが steering 記述と一致)
├── prisma.config.ts            # 設定ファイル(seed 用)
├── package.json                # 後述
└── public/                     # 静的アセット
  • 欠如: app/candidate/[id]/, app/api/, app/actions/, features/, prisma/schema.prisma, prisma/migrations/, components/public/, types/, auth.ts, proxy.ts, lib/prisma.ts, lib/microcms.ts, lib/voting-period.ts, lib/date.ts のすべてが未作成。
  • Boundary Map との関係: design.md の File Structure Plan で新規追加すべきファイル(app/candidate/[id]/page.tsx, app/candidate/[id]/_components/CandidateDetailClient.tsx, features/candidates/hooks/useImageSlider.ts, components/public/{DetailHeader,CandidateImageSlider,CandidateInfo,VoterRanking,PaidVoteButton}.tsx)はすべて未存在。Modified Files は本 spec の design でも「なし」と明示されている。

1.2 既存 candidate / vote 関連の痕跡

  • Grep で大文字小文字無視で candidate|Candidate|microcms|prisma|voting|vote.kiro / node_modules / .git / docs-site / lock files 除外で検索した結果、ヒットは package.json / prisma.config.ts / app/globals.css(CSS 変数名のみ) / CLAUDE.md / .claude/skills/**設定・指針系のみ。実装コードには痕跡ゼロ。
  • これは前回(2026-05-09 頃の大規模リセット)の意図と整合する: 過去の features/voting/services/voterService.ts, features/voting/services/votingPeriodService.ts, candidateService.getAllWithRank(), app/api/images/[id]/** といった「再導入禁止」対象が物理的に存在しない状態を維持できている。

1.3 アーキテクチャ・規約の現状

  • app/layout.tsx: next/font/google で Cormorant Garamond / Jost を --font-display / --font-body に bind 済み。steering tech.md の指針と一致。
  • app/providers.tsx: "use client" だが Provider 集約は未着手で、<>{children}</> のみ。本 spec は新規 Provider を導入しないので影響なし。
  • app/globals.css: 中身は未確認だが design-tokens/tokens.css を読み込んでいる想定(structure.md の単一ソース原則と一致)。
  • tsconfig.json: import エイリアス(@/*, @/features/*, @/components/*, @/design-tokens/*)は structure.md の記述通り設定済みと想定(未確認だが lint / build を通すには必須)。

1.4 ルートレベル設定ファイル

  • next.config.ts: 中身未確認。microCMS CDN を images.remotePatterns に追加する必要(design.md の Performance 節 + tech.md の方針)。本 spec の Modified Files には含まれないが、candidate-detail の画像描画時に必要な前提として data-model または別タスクで処理されているか要確認。
  • eslint.config.mjs / postcss.config.mjs: 直接編集可リスト(CLAUDE.md)の範囲。
  • docker-compose.yml: Neon 互換のローカル DB(postgres:17)用。本 spec 単体では DB を起動しないが、E2E や統合テスト時に必要。

1.5 package.json の実装ベース整合性チェック

想定(steering)実態コメント
next 15+next 16.2.4steering より新しい。App Router の API 変化(params: Promise<...>)はすでに design.md に反映済み(await params)。
react 1919.2.4一致。useOptimistic を本 spec が利用。
prisma 7.8.0 + @prisma/client 7.8.0一致data-model spec 側で利用。
stripe 22.1.0 + @stripe/stripe-js 9.3.1導入済みvoting spec 側で利用。
@stripe/react-stripe-js未導入voting spec の Payment Element 表示に必要。本 spec は import しないが、<PurchaseFlowContainer> を経由する以上、voting spec 実装時に追加必要。
microcms-js-sdk未導入data-model spec の candidateService / creditPackageService が利用。本 spec の getCandidate / getCandidates 呼び出しはすべて data-model 側経由なので、本 spec の impl 開始前に data-model 完了が必須。
@upstash/ratelimit + @upstash/redis未導入voting spec のレート制限で利用。本 spec には直接関係しないが、voting spec の前提。
react-slick + slick-carousel導入済みtech.md は候補者詳細のメインスライダーとして react-slick を挙げているが、本 spec の design.md は useImageSlider 純フック + サムネイル + next/image で組み立てる方針。react-slick 採用かどうかは design.md では暗黙。tasks 2.2 にも react-slick の指定なし → 自前実装で良い余地あり。要 design/レビュー時の確認。
motion 12.x一致Confetti を motion で実装可。
lucide-react 0.487.0+ (steering)^1.12.0異常。lucide-react は本来 0.4xx 系。^1.12.0 は別パッケージかフォーク。本 spec のアイコン使用は ui-design.md 委譲だが、依存先パッケージ確認が必要。
shadcn 4.6.0導入済みCLI 名と同名のランタイム依存は通常存在しない。要確認だが本 spec の影響は最小。
zod 4.3.6導入済み本 spec は zod を直接使わない(voting/data-model 側)。
react-hook-form + @hookform/resolvers導入済み本 spec は使用しない。
@dnd-kit/* / csv-parse / @base-ui/react導入済み本 spec とは無関係。past artifacts と思われる。

本 spec はパッケージ追加を伴わない。アイコンを使う場合に lucide-react の不一致がブロッカーになり得るので、ui-design.md と合わせて確認すること。

2. Requirements Feasibility Analysis (Requirement-to-Asset Map)

凡例: ✅ 既設計で完全カバー / 🟡 既設計あり・依存 spec 未実装 / 🔴 Missing / ❓ Unknown(要研究) / 🧱 Constraint

ReqAsset(設計上)既存実装StatusNotes
1.1 ID で候補者取得data-model: getCandidate(id) 経由で page.tsxなし🟡data-model spec 完了待ち。
1.2 404 フォールバックpage.tsx notFound() + 二段判定なし🟡実装は import のみで OK。
1.3 候補者名入りメタデータgenerateMetadataなし🟡Request Memoization に依存。data-model の契約に従う。
2.1 戻り導線<DetailHeader><Link>なし🔴単純実装。
3.1 順位・名前・得票数<DetailHeader> propsなし🔴toLocaleString('ja-JP') で 3 桁区切り。
3.2 ページ表示時点の順位rankCandidates + getVoteCountsByCandidateIdsなし🟡candidate-ranking + data-model 完了待ち。
3.3 投票後 voteCount のみ更新useOptimistic reducerなし🔴reducer 内で rank に触れない明示テスト必要(tasks 4.3 で検証)。
3.4 順位は再訪問時に最新化Server 算出なし🔴Client 側で rank を再計算しない。
4.1 投票アクション UI<PaidVoteButton> + useVotingPeriodStatusなし🟡voting spec の useVotingPeriodStatus 完了待ち。
5.1-5.5 画像ギャラリー<CandidateImageSlider> + useImageSliderなし🔴純フックは自前実装。hasMultiple 判定で 1 枚時に切替 UI 抑止。
6.1-6.2 プロフィール表示<CandidateInfo>なし🔴Candidate 型の region / occupation / height / hobbies[] / certifications[] 必須。data-model 完了待ち。
7.1-7.2 PR メッセージ<CandidateInfo>なし🔴white-space: pre-line で改行保持。
8.1-8.4 応援者ランキング<VoterRanking> + getTopVotersByCandidateId(id, 5)なし🟡data-model spec で getTopVotersByCandidateId 新規定義必要(research.md Topic 1 で既出)。
9.1 投票成功フィードバック<Confetti> + useVoteFlowなし🟡voting spec の useVoteFlow.handlePaidVoteConfirmed 完了待ち。<Confetti> は home spec 所有でも可(tasks 3.1)。
非機能-メタデータgenerateMetadata OGPなし🟡1.3 と同じ。
全画面共通: 戻るボタン / フッター / Scroll Toplayout 側 or 共通 components未確認product.md 全画面共通要件。本 spec の責務外だが、layout 完成度を要確認。

Constraints / Unknowns

  • 🧱 保護パス制約: features/**, app/candidate/**, app/_components/**, components/public/**, auth.ts, prisma/schema.prisma, lib/prisma.ts, types/**/kiro-impl 経由のみ。本 spec の impl がこれらの新規作成権限を持つ。
  • 🧱 Request Memoization 契約: data-model 側で getCandidateReact cache()fetch next.revalidate を直接使う実装でなければ、generateMetadatapage.tsx で二重取得が発生。本 spec の Performance 想定が崩れるので data-model design に明示が必要(既に design.md Allowed Dependencies / Revalidation Triggers に記載済み)。
  • 🧱 Vote.nickname 正規化済み保存: 応援者ランキング集計の正しさは voting spec の Webhook ハンドラが normalizeNickname 適用済み値を保存していることに依存(design.md Req 8.3 で明示済み)。
  • react-slick 採用可否: 本 spec の design.md は自前 useImageSlider を採用するが、tech.md は react-slick を挙げている。react-slick を採用しない場合、tech.md との乖離をどうするか要判断(steering 更新 or design 維持)。
  • microCMS images.remotePatterns 設定: next.config.ts への microCMS CDN ホスト追加は誰が行うか(data-model / 全画面共通)。本 spec impl 時に未設定だと next/image が拒否する。前提タスクとして要確認
  • <Confetti> の所有 spec: design.md は votinghome spec 所有を示唆。tasks 3.1 に「<Confetti>(home spec の Confetti を共有してよい)」と明記。home spec の Confetti 実装状況を要確認。
  • 共通 layout の Footer / ScrollToTopButton: 詳細画面でも product.md の全画面共通 UI を提供する必要。本 spec の Modified Files は「なし」なので、layout 側の整備状況に依存。

複雑度シグナル

  • Simple CRUD: なし(本 spec は描画専用)。
  • Algorithmic: 順位算出は別 spec 所有の純関数を再利用するため、本 spec 内に複雑なアルゴリズムは持たない。
  • Workflow: 投票成功時の楽観更新フローを 1 つ持つが、状態機械は useVoteFlow(voting 所有)に集約。
  • External integration: microCMS / Neon / Vercel ENV は すべて data-model 経由で間接アクセス。本 spec から直接外部呼び出しはなし。

3. Implementation Approach Options

Option A: 既存ファイルを拡張する

該当ファイル: なし。本 spec が触る範囲はすべて新規作成のため、Option A の前提が成立しない。

Trade-offs: N/A(候補からは除外)。

Option B: 新規ファイルのみで構築する(design.md の File Structure Plan に従う) — 第一候補

新規作成ファイル:

  • app/candidate/[id]/page.tsx(Server Component)
  • app/candidate/[id]/_components/CandidateDetailClient.tsx(Client)
  • features/candidates/hooks/useImageSlider.ts(純フック)
  • components/public/DetailHeader.tsx
  • components/public/CandidateImageSlider.tsx
  • components/public/CandidateInfo.tsx
  • components/public/VoterRanking.tsx
  • components/public/PaidVoteButton.tsx

統合ポイント:

  • 上記すべての import 先(@/features/cms/services/candidateService 等)は data-model / candidate-ranking / voting spec が所有・実装する。
  • 本 spec の impl 時点で前提 spec が /kiro-impl 済みであることを必須条件とする(tasks.md 冒頭の前提宣言と一致)。

Trade-offs:

  • ✅ design / tasks と完全一致。レビューが最も簡素。
  • ✅ 保護パス境界が明確で、/kiro-impl candidate-detail の自律実行に乗りやすい。
  • ✅ 既存実装ゼロのため、後方互換性の懸念がない。
  • ❌ 前提 spec(data-model, candidate-ranking, voting)未完了のままでは本 spec を起動できない(直列依存)。

推奨: 第一候補。tasks.md の Foundation 1.1 → Core 2.x → Integration 3.x → 検証 4.x の順で進める。

Option C: ハイブリッド(本 spec のスタブを先行作成し、前提 spec を平行で進める)

戦略:

  • 本 spec の components/public/*.tsx依存 import なしの presentation-only stub として先に作成し、props 契約のみ確定。
  • 並行で data-model / voting / candidate-ranking spec を /kiro-impl で進める。
  • 前提が揃った段階で app/candidate/[id]/page.tsx<CandidateDetailClient> を Integration する。

Trade-offs:

  • ✅ UI レビュー(Storybook 想定)を先行できる。
  • ✅ ui-design.md の検証を並走できる。
  • ❌ 保護パス制約により features/candidates/hooks/useImageSlider.ts 等は /kiro-impl candidate-detail 経由でしか作れないため、stub と本実装で /kiro-impl を 2 回回す必要がある(workflow 上のオーバーヘッド)。
  • ❌ tasks.md の _Depends 順序を分断するため、autonomous mode との相性が悪い。
  • ❌ 前提 spec が完成すれば Option B との差が消えるため、得るものが少ない。

推奨度: 低。並走の必要が明示された場合のみ採用。

4. Effort & Risk

観点評価根拠(1 行)
EffortM(3–7 日)新規 5 コンポーネント + 1 純フック + 1 Server Component + generateMetadata + テスト群。前提 spec が揃えば 1 ファイル 1〜4 時間。
RiskMedium単体パターンは確立済みだが、(a) 前提 spec の契約変更追従、(b) useOptimistic での rank 不変保証、(c) Request Memoization に頼った Performance 想定、の 3 点が外部要因で揺れる可能性。

リスクと緩和策

  • R1 前提 spec 未完了: data-model / candidate-ranking / voting のいずれかが impl 未完了なら本 spec を起動しない。/kiro-spec-status で確認してから /kiro-impl candidate-detail する。
  • R2 Request Memoization 失効: data-model 側の実装が cache() or fetch revalidate でないと、generateMetadatapage.tsx で microCMS を二重に叩く。Performance 想定が崩れたら本 spec の Revalidation Trigger 発火 → data-model 修正 → 本 spec 再検証。
  • R3 useOptimistic reducer の rank 上書き混入: tasks 4.3 で「rank 不変」を必ずアサート。レビュー観点でも明示。
  • R4 react-slick vs 自前 slider の方針乖離: tech.md と design.md の整合性レビューを ui-design.md 確認時に同時実施。
  • R5 microCMS images.remotePatterns 未設定: 本 spec の impl 時に next/image が microCMS CDN を拒否してビルドが落ちる。data-model spec か別の全画面共通タスクで next.config.ts に host を追加しておくこと。
  • R6 <Confetti> の所有 spec 不明: home spec 側の実装が未着手なら、本 spec で <Confetti> をどう調達するか追加判断が必要。tasks 3.1 は home 側を再利用する書き方になっているので、home spec の status を要確認。
  • R7 共通 layout 整備: フッター・ScrollToTopButton・候補者一覧/ランキング戻り導線(Header)などの全画面共通 UI が app/layout.tsx に未組み込み。本 spec の Header は <DetailHeader> 内で戻り導線を持つ設計だが、全画面共通フッターは別管轄。

5. Recommendations for Design Phase / Impl Phase

design / tasks はすでに承認済みのため、design phase の再起動は不要。以下は /kiro-impl candidate-detail を回す前のチェックリストとして提供する。

起動前チェックリスト

  1. /kiro-spec-status data-modeltaskscompleted(かつ実装ファイルが存在)であることを確認。
  2. /kiro-spec-status candidate-rankingtaskscompleted(rankCandidates / RankedCandidate が export 済み)。
  3. /kiro-spec-status votingtaskscompleted(useVoteFlow / useVotingPeriodStatus / <PurchaseFlowContainer> が import 可能)。
  4. next.config.tsimages.remotePatterns に microCMS CDN ホストが含まれていること(なければ data-model または別 spec で追加)。
  5. package.jsonmicrocms-js-sdk / @stripe/react-stripe-js / @upstash/ratelimit / @upstash/redis が追加されていること(なければ前提 spec の impl で追加されるはず)。
  6. lucide-react ^1.12.0 の正体を確認(本物 lucide-react か別パッケージか)。アイコン採用箇所が ui-design.md にあれば必須。
  7. <Confetti> の所有 spec が決定済み(home / voting / candidate-detail のいずれか)。

推奨アプローチ

  • Option B(新規ファイル)を採用。tasks.md の _Depends を尊重し、autonomous mode(/kiro-impl candidate-detail)で subagent per task に任せる。
  • 並走の必要があれば Option C を取るが、/kiro-impl の境界を 2 回横断するため工程管理コストが上がる点を許容できる場合に限る。

Research items to carry forward(impl 中に再評価)

  1. 画像スライダーの実装ライブラリ選定: react-slick を採用するか自前 useImageSlider で完結させるか。ui-design.md と design.md tasks 2.2 を突き合わせて確定する。
  2. <Confetti> の依存解決: home spec 側で実装されている場合は import、未実装なら本 spec で同時実装するか home 側の /kiro-impl を先行する。
  3. layout 側の全画面共通要素: Footer / ScrollToTopButton の所有 spec を確認し、本 spec impl 中に layout が空のままなら一時的に candidate-detail 範囲外として記録する。
  4. next.config.tsimages.remotePatterns: data-model spec の impl 段階で確実に設定されたか、本 spec impl 開始時に再確認。
  5. Request Memoization の実証: data-model 側の getCandidate 実装が cache() ベースか fetch ベースかを確認し、本 spec の Performance 想定が成り立つことを実機で確認(tasks 3.2 の Network タブ確認手順)。

Output Checklist 達成状況

  • [x] Requirement-to-Asset Map(Section 2)— Missing / Unknown / Constraint タグ付き
  • [x] Options A/B/C(Section 3)— rationale + trade-offs
  • [x] Effort(M) / Risk(Medium)— 1 行根拠
  • [x] Recommendations(Section 5)— 推奨アプローチ + research items