エグゼクティブサマリー
複数の焙煎所を買い回る愛好家のための「コーヒー履歴書」を育て、まだ知らない最高の豆との出会いを毎週の習慣にするプロダクト。自社で豆を売らず(在庫を持たず)、最高の豆があるショップへ中立に送客する「発見プラットフォーム」を志向する。
北極星指標は月間「出会い数」(新規に記録された豆の種類数)。ただしMVP(NOWフェーズ)の最優先は、その入力指標である記録の習慣化を証明すること。戦略上の最大リスクは「記録の手間>価値になり習慣化しない」ことであり、本フェーズの機能・OKR・技術選定はすべて記録の摩擦を極限まで下げる一点に収束している。
記録経路は2つを補完的に用意する。オンライン購入は「見ている商品ページからサーバーサイドAI抽出(Cloudflare /crawl)でワンタップ取り込み」、店頭・他所購入は「袋を撮影してAI抽出」。抽出が失敗しても記録は残す楽観更新を貫き、摩擦で記録を失わせない。回遊・レコメンド・社会的発見といった「発見エンジン」はNEXT以降に位置づけ、NOWでは混ぜない。
主要コンセプト
本プロダクトを読み解くための中核用語。以降の各セクションはこれらを前提に記述する。
戦略サマリー
最高のスペシャリティコーヒーとの出会いを、毎週の習慣にする。掲げる価値は発見>効率/送客の中立性(自社で売らない)/記録は資産。
ターゲットと立ち位置
一次セグメントはマルチショップ・ジプシー(課題の熱量が最も高く、購入種類が多くデータが濃く、口コミ伝播力がある/PM自身がユーザーでドッグフーディング可能)。相対コストはプレミアム価値型で、安さではなく「あなたにとっての最高の一杯への最短距離」を売る。明示的に最初は狙わない層=大手チェーン中心のライトユーザー、業務用バイヤー。
戦略的トレードオフ(やらないこと)
戦略の本質は「何をやらないか」。下表が価値を増幅する意思決定である。
| やらない | 代わりに | なぜ |
|---|---|---|
| 自社EC・決済を持つ | ショップへの送客に徹する | 在庫リスク・オペコストを負わず、中立な発見プラットフォームの信頼を守る |
| 抽出レシピ/抽出ログの深掘り | 「豆との出会い」に集中 | 既存の抽出系アプリと正面衝突せず、コア体験を薄めない |
| 大型SNS/コミュニティ化 | 個人の記録体験を磨く | 初期はネットワーク前提機能に依存せず、単独でも価値が立つ設計(※社会的発見は匿名共起で両立を検証) |
| 大手チェーンの網羅 | スペシャリティ特化 | ライト層は課題が浅く差別化が効かない |
| 多機能化(バリスタ向け計測等) | 記録→発見のループに一点集中 | MVPでの習慣化検証を最優先、機能の散漫化を防ぐ |
成長エンジンと防御性
基本は Product-Led Growth。記録が貯まるほどレコメンド精度が上がり送客が増え、提携が広がって記録対象も広がるループを回す。
防御性:データモート(嗜好履歴のスイッチングコスト)/ネットワーク効果(記録が豆マスタとレコメンド精度を共同で高める)/中立ブランド(自社ECを持つ競合には取りづらい)/送客実績に基づくショップ関係。MVP段階ではモートは薄く、守り方は「ニッチ深耕 × ショップ関係 × データ密度」の三重化。
出典:プロダクト戦略ノート(要約・一部保持)
ロードマップ
機能ではなくアウトカム(誰が・何をできるように・なぜ)で記述。各フェーズは前フェーズの検証済み仮説を土台に開く。前が証明できなければ次へ進まない。
記録の習慣化を証明する
OMTM: 記録継続率"] X["NEXT / v1
回遊と発見をつなぐ
North Star: 出会い数"] L["LATER
発見をスケールし送客で稼ぐ
マネタイズ検証"] N --> X --> L style N fill:#e7ecff,color:#000 style X fill:#635bff,color:#fff style L fill:#4b45c6,color:#fff
| フェーズ | アウトカム | 主な取り組み |
|---|---|---|
| NOW | 愛好家が「買ったら記録する」を自然に続けられ、記録継続率が立ち上がる | 豆記録コアスキーマ/最小WebView+ページ抽出(数店限定)/袋撮影フォールバック/好み先行の初回起動+コールドスタート推薦「あなたにおすすめの豆」(§3.5・手動シード)/計測基盤 |
| NEXT | 回遊と履歴ベースの発見で、1人あたりの出会い数を増やす | WebView回遊+お気に入り管理/コーヒー履歴書ビュー/回遊・キュレーション型の発見(コールドスタート好み推薦はNOWへ前倒し済)/item-based 社会的発見(検証ゲート通過後) |
| LATER | レコメンド自動化と送客で発見の質とマネタイズを両立、防御性を厚くする | レコメンド自動化(協調フィルタ)/ショップ提携・送客連携/豆マスタのクラウドソース化 |
NOW フェーズ — ビルド順とアウトカム(依存駆動)
NOW の中身を、図4の通し依存マップ(基盤→土台→周辺→入力→主経路→回復)に沿って着手順に並べ替えた。各層は「誰が・何を・なぜ」のアウトカムと、動かす KR で定義する。L1(US-1 手動記録=要石スキーマ)が後段の大半をブロックする最重要ノード。
| 層(ビルド順) | アウトカム(誰が・何を・なぜ) | 含む US | 動かす指標 |
|---|---|---|---|
| L0 基盤 | 愛好家が複数端末でも自分の記録に安全にアクセスでき、記録が資産として失われない土台を作る。 | Must US-11 |
以降全KRの前提 |
| L1 土台 | 何を買ったかを ≤30秒で確実に残せる(要石スキーマ=抽出・記録・推薦の共通契約を確定)。抽出が無くても記録は残り、習慣化の最小ループが成立する。 | Must US-1 |
記録1件 ≤60秒 / 離脱 ≤30% |
| L2 周辺・好み 並列可/内部順 US-15→US-16→US-8 |
記録を見返し・直し、計測で習慣化を可視化。対象店・カメラ権限・公開設定の前提を整え、好み登録→おすすめ→初日に1袋記録で Activation を取る。 | Must US-9 US-8Should US-2 US-10 US-12 US-13 US-15 US-16Could US-3 |
KR1 Activation / KR2 継続 |
| L3 入力経路 | お気に入り店を見ながら/袋を撮るだけで記録を起こせる。オンライン・店頭の双方で記録の摩擦を下げる。 | Should US-4 US-7Could US-14(スパイク要) |
入力経路別の記録成立率 |
| L4 主経路抽出 | 見ている商品ページからワンタップで自動充填でき、オンライン記録の摩擦が最小化する。 | Must US-5 |
充足率 80% / 取り込み ≤10秒 / KR3 密度 |
| L5 回復 | 抽出が失敗・拒否されても作りかけの記録を失わない。「摩擦で記録を失わせない」原則を全経路で守る。 | Should US-6 |
抽出失敗時の記録保全率 |
NOW → NEXT:記録系仮説 H1〜H5 の検証+OKR(Activation 60% / 週次継続 40% / 月4袋)の確認。
NEXT → LATER:社会的発見 C1〜C3 が検証ゲートを通過し、協調フィルタに足る母数(N)が貯まること。
§3.5 のコールドスタート好み推薦は手動シードで NOW に前倒し済(カタログ自動クロールのみ E7 ゲート後=NEXT)。
記録データ(NOW)→ レコメンド精度(LATER)。協調フィルタリングはユーザー数に依存するため、NEXT で母数が貯まるまで自動化を急がない。
出典:アウトカムロードマップ ノート(保持・要約)
OKR(MVP期)
3案(習慣化 / 課題の確信 / 摩擦最小化)から案A(習慣化)を主OKRに採用。案Cのガードレールを併走、案BのKR1(インタビュー)を着手前チェックとして先行実施する。
Objective:愛好家が「買ったら記録する」を、無意識の習慣にする。
| ID | Key Result | ターゲット | 位置づけ |
|---|---|---|---|
KR1 | 初回豆記録の Activation 率(登録〜初日に1袋記録) | 60%以上 | 入口 |
KR2 | 記録ユーザーの週次継続率 W1→W4(=OMTM) | 40%以上 | 定着 |
KR3 | アクティブユーザー1人あたり月間記録豆数 | 平均4袋以上 | 密度 |
記録1件あたり所要時間 ≤ 60秒/記録の途中離脱率 ≤ 30%。速度を追って正確性や信頼を犠牲にしない。
MVP期はユーザー数(N)が小さく率指標はブレやすい。各KRは仮説的ターゲット(自信度60〜70%)であり、率と併せて実数でも追う。
不採用案(参考)
| 案 | 賭けどころ | 検証仮説 | 向くタイミング |
|---|---|---|---|
| B | 課題が実在するか | H1 | まだ作る前/確信が薄い段階 |
| C | 摩擦を消せるか | H4 | 記録UXを磨く段階 |
出典:OKR(MVP期)ノート(保持)
主要ワークフロー
記録には3経路がある。常に「記録を先に作る」楽観更新を貫き、抽出は後追いで充填する。
フローA:オンライン購入 → ページ抽出(主経路)
- 記録の即時作成 — 「記録」タップで
source_url付きの記録を先に作る(楽観更新)。 - サーバーサイド抽出 — Worker/Edge Function が
/crawlを実行。トークンはサーバ内に秘匿。 - 自動充填と確認 — スキーマ各項目を充填、記載のない項目は
null(推測で埋めない)。ユーザーが確認・微修正して保存。 - ガードレール — 取り込み〜充填は
10秒以内。失敗・タイムアウトでも記録は残りフォールバックへ。
フローB/C:袋撮影・手動(補完経路)
- フローB(撮影):店頭・他所購入は袋を撮影 → Vision LLM で抽出 → 自動充填 → 確認・保存(
≤ 60秒)。画像はimage_urlとして保存。 - フローC(手動):抽出が拒否/低精度のときの最終手段。空フォームに手入力(必須のみで
≤ 30秒)。
抽出が失敗しても記録は残り、ユーザーは手で埋められる。摩擦で記録を失わせないことが記録習慣化の生命線。
出典:PRD §7.1 / ユーザーストーリー(保持)
機能要件
PRD §7.2 を INVEST/3C に沿って分割したユーザーストーリー。優先度は OKR 案A の KR への直結度で決定(P0=Must / P1=Should / P2=Could)。網羅性レビュー(2026-06-21)でアクター/エンティティ×CRUD/ジャーニー/状態の4レンズで突合し、欠けていた前提・周辺を 網羅補完 として US-10〜14 で追加(計14ストーリー)。各USの詳細はサイドバー「機能要件」配下に展開。さらに 2026-06-26 のオンボーディング再設計で、§3.5(コールドスタート好みレコメンド)を NOW(手動シード)へ前倒し:初回起動を「好み登録→おすすめ→初回記録」に作り替え(US-8 改)、申告好みの登録・編集(US-15)と常設の推薦面「あなたにおすすめの豆」(US-16)を追加。Activation(KR1)は初回記録(推薦経由)を維持。自動化(カタログ自動クロール)のみ E7 ゲート後=NEXT。
| ID | ストーリー | 優先 | 状態 | 主な受け入れ条件 / KR直結 |
|---|---|---|---|---|
US-1 | 豆を手動で記録する(土台) | Must | Confirmed | 必須=name/shop/purchased_at、任意は空可、enum選択、最小入力 ≤30秒 |
US-8 | 初回起動で好みを登録し、おすすめから初日に1袋記録 | Must | Confirmed | 好み登録(スキップ可)→おすすめ→推薦経由で初回記録/送客、達成FB / Activation(推薦経由/直接を区別) |
US-9 | 記録イベントを計測する(PM視点) | Must | Confirmed | Activation/継続/所要時間/抽出品質を非同期計測 / 全KR |
US-5 | 商品ページから取り込み自動充填(主経路) | Must | Confirmed | 楽観更新→解析中→自動充填→確認保存、取り込み ≤10秒 / 充足率80% |
US-2 | 記録した豆を一覧・詳細で見返す | Should | Confirmed | 新しい順一覧、詳細、source_urlから再注文、空状態 / 継続 |
US-4 | WebViewでお気に入り店の商品ページを開く | Should | Confirmed | 数店限定、現在URL取得して記録へ渡す / カバレッジ |
US-7 | 袋を撮影して情報を自動充填する | Should | Confirmed | 撮影→抽出充填→確認保存、image_url保存、撮影〜保存 ≤60秒 |
US-6 | 抽出不可時に撮影/手動へフォールバック | Should | Confirmed | robots拒否は撮影案内、作りかけの記録を失わない |
US-3 | 記録した豆を編集・削除する | Could | Confirmed | 全項目編集、削除確認、自動充填値の上書き / 信頼性 |
US-11 | アカウント作成・ログイン・ログアウト 網羅補完 | Must | Confirmed | 外部認証(Supabase)ラッパー、セッション永続化、user_id+RLS紐付け / 複数端末の土台 |
US-10 | お気に入り店(対象店)を登録・編集・削除 網羅補完 | Should | Confirmed | 店名+URL登録、WebView許可リスト連携、抽出不可フラグ / US-4の前提(回遊管理はNEXT対象外) |
US-13 | カメラ権限の要求と拒否時フォールバック 網羅補完 | Should | Confirmed | 権限要求→拒否時は写真選択/手動へ、再許可導線、クラッシュしない / US-7の前提 |
US-12 | 公開設定(visibility)の布石・既定private 網羅補完 | Should | Confirmed | visibilityカラム追加、既定private、§9集計の除外保証 / 社会的発見NEXTの前提 |
US-14 | オフライン記録→復帰後の抽出・同期 網羅補完 | Could | スパイク要 | オフライン一次保存、復帰時に冪等同期、source_url記録は復帰後 /crawl 充填 / 信頼性 |
US-15 | 好みを登録・編集する(コールドスタート好みの入力) §3.5 NOW | Should | Confirmed | 申告好み(焙煎度/フレーバー/産地)の登録・編集・リセット、スキップ可、挽き方除外 / US-8初回・US-16推薦の入力 |
US-16 | ホーム「あなたにおすすめの豆」で好みに合う豆と出会う §3.5 NOW(手動シード) | Should | 設計段階 | 好み×今買えるカタログを照合→推薦カード→記録/送客、空状態フォールバック / 転換率・一致率(E7) |
実装サブタスク分解(リファインメント:2026-06-21)
全14 US を「1サブタスク≦2〜3日・縦切り」に分解(計 62サブタスク)。各USの詳細はサイドバー「機能要件」配下の個別ページに展開。見積は下記スパイク後。
| スパイク | 内容 | サイズ | 紐づくUS |
|---|---|---|---|
US-5.0 | /crawl で代表3〜5店の項目自動充足率80%+充填10秒を実測(=H5・主経路の生死判定) | M(~2d) | US-5 |
US-7.0 | 画像→構造化抽出の方式選定(Vision LLM / OCR / Workers AI)。/crawlは画像入力非対応のため別系統が前提 | M(~2d) | US-7 |
US-14.0 | ローカルDB(WatermelonDB/expo-sqlite等)+同期戦略(冪等upsert・競合解決・復帰検知) | L(~3d) | US-14 |
US-11.1 | Expo×Supabase Auth のリダイレクト/Deep Link 検証(Expo Go と Dev Build の挙動差) | M(~2d) | US-11 |
所見:① /crawl は画像非対応 → US-7 は別抽出系統。② 「匿名先行利用」(US-11 Open Q)の可否が US-1/US-8 の起点まで連鎖する最高レバレッジ判断。
出典:ユーザーストーリー ノート(網羅性レビュー+リファインメントで更新) / PRD §7.2
US-1:豆を手動で記録する(土台) P0 / Must
Description
愛好家として、買った豆の情報(産地・焙煎度・購入元など)を手で記録したい。袋を見ながら確実に残せるように。
受け入れ条件
- 記録フォームに豆スキーマ(PRD 7.3)の各項目が入力できる。必須は
name/shop/purchased_at(既定=当日)。 - 任意項目(産地・品種・精製・焙煎度・焙煎日・内容量・価格・テイスティングノート・メモ・画像)は空のまま保存できる。
- 保存すると記録が永続化され、再起動後も残る。
purchased_atは既定で当日が入り、ワンタップで保存まで到達できる。process/roast_levelは選択肢(enum)から選べる。- (ガードレール)最小入力(必須のみ)で30秒以内に保存完了できる。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-1.1 | 豆スキーマのデータモデル定義+Supabase テーブル/マイグレーション(PRD7.3全項目・RLSでユーザー別保護) | S(~1d) | US-11 | 1,3 |
US-1.2 | 記録フォームUI(必須3項目+任意、process/roast_level は enum picker、purchased_at既定=当日) | M(~2d) | US-1.1 | 1,2,5 |
US-1.3 | 保存・永続化ロジック(Supabase insert+ローカル反映、再起動後保持) | S(~1d) | US-1.1 | 3 |
US-1.4 | 最小入力=30秒フロー+所要時間計測フック(US-9連携・PostHog) | S(~1d) | US-1.2 | 4,6 |
US-2:記録した豆を一覧・詳細で見返す P1 / Should
NOTE:好みの傾向可視化(コーヒー履歴書)はNEXT。ここは最小の一覧+詳細に限定。
Description
愛好家として、記録した豆を一覧で見返し、1件をタップして詳細を見たい。「残っている」価値を実感できるように。
受け入れ条件
- 記録した豆が新しい順に一覧表示される(銘柄・店・購入日・サムネ)。
- 1件タップで全項目の詳細が見られる。
source_urlがあれば、詳細から元の商品ページを開ける(再注文導線)。- 1件も記録がないときは、最初の記録を促す空状態が表示される。
- 画像があればサムネ表示、なければプレースホルダ。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-2.1 | 新しい順一覧(銘柄/店/購入日/サムネ、TanStack Query、空状態、画像/プレースホルダ) | M(~2d) | US-1 | 1,4,5 |
US-2.2 | 詳細画面(全項目、Expo Router typed route、null項目の表示統一、解析中表示配慮) | M(~2d) | US-2.1 | 2 |
US-2.3 | source_url 商品ページを開く導線(Linking・URLバリデート・再注文計測) | S(~1d) | US-2.2 | 3 |
❓ Open Q:空状態(AC4)の誘導先はUS-8と重複しうる。一覧空状態は単純導線に限定し初回フローはUS-8へ寄せるか。
US-3:記録した豆を編集・削除する P2 / Could
Description
愛好家として、記録した豆を後から編集・削除したい。自動充填の間違いや入力ミスを直せるように。
受け入れ条件
- 詳細から編集に入り、全項目を修正して保存できる。
- 削除でき、削除前に確認が出る。
- 編集内容は即時反映され、永続化される。
- 自動充填で入った値も手で上書きできる。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-3.1 | 編集(US-1フォーム再利用・プリフィル・update mutation・即時反映・自動充填値上書き) | M(~2d) | US-1, US-2.2 | 1,3,4 |
US-3.2 | 削除(確認ダイアログ・delete・楽観更新+ロールバック・画像実体削除方針) | S(~1d) | US-2.2 | 2 |
❓ Open Q:削除時にSupabase Storage上の袋写真も物理削除するか(孤児ファイル許容か)。
US-4:WebViewでお気に入り店の商品ページを開く P1 / Should
NOTE:回遊・お気に入り管理という発見体験はNEXT。ここは記録のための最小WebView(指定URL/数店を開けるだけ)。
Description
愛好家として、アプリ内のWebViewでお気に入り店の商品ページを閲覧したい。タブを開かずそのまま記録に進めるように。
受け入れ条件
- 登録した数店の商品ページをアプリ内WebViewで表示できる。
- 一般的なページ操作(スクロール・リンク遷移・戻る)ができる。
- 表示中ページのURLが取得でき、「記録」アクションに渡せる。
- 対応店は数店に限定(NEXTで拡大)。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-4.1 | react-native-webview組込+対象店ホワイトリスト表示(対象外URLは拒否) | M(~2d) | US-10 | 1,4 |
US-4.2 | 一般操作(スクロール/遷移/戻る/再読込/外部ブラウザ、履歴管理) | S(~1d) | US-4.1 | 2 |
US-4.3 | 表示中URL取得→「記録」へ source_url 受け渡し | S(~1d) | US-4.1 | 3 |
❓ Open Q:ホワイトリストの管理場所(Supabase shops か同梱定数)。SPA系ECでのURL取得漏れ検証。
US-5:見ている商品ページから情報を取り込んで自動充填する(主経路) P0 / Must
Description
愛好家として、WebViewで見ている商品ページからワンタップで豆情報を取り込み、記録フォームに自動充填したい。入力の手間をほぼゼロにできるように。
受け入れ条件
- 「記録」タップでまず記録が即時作成され、
source_urlが保存される(楽観更新)。 - 抽出中は「解析中」が表示され、ユーザーは待たずに他操作できる。
- 抽出完了で、スキーマ各項目がフォームに自動充填される(Cloudflare
/crawl、サーバーサイド)。 - 充填後、ユーザーが確認・微修正して保存できる。
- 記載のない項目は空(null)で、推測値は入れない。
- (ガードレール)取り込み〜充填が10秒以内に完了する。
- 抽出失敗・タイムアウト時は記録自体は残り、US-6のフォールバックへ誘導する。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-5.0 | 【スパイク】代表3〜5店で /crawl充足率80%+充填10秒を実測、prompt/Schema初版確定 | M(~2d) | US-4 | 3,5,6 |
US-5.1 | Worker /crawlラッパー(Schema+prompt契約固定・jobId返却・null固定/推測禁止) | M(~2d) | US-5.0 | 3,5 |
US-5.2 | 非同期ポーリング/結果取得(processing/done/failed・10秒境界・failed正規化) | M(~2d) | US-5.1 | 6,7 |
US-5.3 | 楽観更新(「記録」タップで即時作成+source_url保存、裏でジョブ起動) | S(~1d) | US-4, US-5.1 | 1 |
US-5.4 | 「解析中」非ブロッキングUI(待ち中も編集可・ジョブ状態購読) | M(~2d) | US-5.2,5.3 | 2 |
US-5.5 | 完了→フォーム自動充填(null項目は空、確認・微修正して保存) | M(~2d) | US-5.4 | 3,4,5 |
US-5.6 | 失敗/タイムアウト処理(記録保持・US-6導線へ誘導・イベント発火) | S(~1d) | US-5.2,5.3 | 7 |
US-5.7 | 計測(レイテンシ/充足率/成功失敗率、10秒・80%ガードレール監視) | S(~1d) | US-5.4,5.5 | 6 |
❓ Open Q:対象店の代表集合は確定済みか。80%未達時の最低成立項目(name/shop/price等)。ポーリング間隔とジョブ保持TTL。
US-6:抽出が使えないときに撮影/手動へフォールバックする P1 / Should
Description
愛好家として、ページ抽出が失敗・拒否・低精度のとき、撮影か手動入力にスムーズに切り替えたい。どんな購入でも記録を諦めずに済むように。
受け入れ条件
- robots.txt拒否などで抽出不可な店では、抽出を試みず撮影/手動を案内する。
- 抽出が失敗/タイムアウトしたら、撮影 or 手動入力の導線が出る。
- どの経路でも作りかけの記録は失われない(再入力不要)。
- 抽出結果が明らかに不十分でも、ユーザーが手で補完して保存できる。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-6.1 | 事前判定(robots不可な店は抽出せず撮影/手動案内、Worker判定→通知) | M(~2d) | US-5.1,5.2 | 1 |
US-6.2 | 失敗後フォールバック導線(US-5.6 failed受領で撮影/手動の選択UI) | S(~1d) | US-5.6 | 2 |
US-6.3 | 記録の引き継ぎ(作りかけ記録・既充填分を保持し再入力不要で遷移) | M(~2d) | US-5.3, US-6.2 | 3 |
US-6.4 | 手動補完保存(部分充填+手入力の混在で保存完了) | S(~1d) | US-6.3 | 4 |
❓ Open Q:「低精度」の判定基準(充足率しきい値 or 必須項目欠落)。撮影経路のOCR有無はMVP範囲か。
US-7:袋を撮影して情報を自動充填する P1 / Should
Description
愛好家として、店頭や他所で買った豆の袋を撮影して情報を自動充填したい。オンライン以外の購入も手早く記録できるように。
受け入れ条件
- 「記録」→ 撮影で袋の写真を撮れる(または既存写真を選べる)。
- 画像から産地・焙煎度等を抽出してフォームに自動充填する。
- 撮影画像は記録の
image_urlとして保存される。 - 充填後、確認・微修正して保存できる。記載なしは空、推測埋めしない。
- (ガードレール)撮影〜保存が60秒以内に完了できる。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-7.0 | 【スパイク】画像抽出方式決定(Vision LLM/OCR/Workers AI)。/crawlは画像非対応=別系統。精度/コスト/60秒評価 | M(~2d) | — | 2,5 |
US-7.1 | 撮影/既存写真選択UI(expo-camera/image-picker、US-13権限連携、ローカル保持) | M(~2d) | US-13 | 1 |
US-7.2 | 画像をSupabase Storageへ上げ image_url 保存(先に記録作成→後追い充填) | M(~2d) | US-7.1 | 3 |
US-7.3 | US-7.0方式で画像→Schema項目抽出→自動充填(記載なしは空・推測埋めしない) | L(~3d) | US-7.0, US-7.1 | 2,4 |
US-7.4 | 充填後の確認・微修正・保存+撮影〜保存60秒計測 | S(~1d) | US-7.3 | 4,5 |
❓ Open Q:抽出はクライアント/Worker どちらか。日本語OCR精度(roast_date等)。抽出失敗時の全項目空フォールバック。
US-8:初回起動で好みを登録し、おすすめから初日に1袋記録する P0 / Must
NOTE:本アプリはオンボーディングが要るほど複雑ではない。重いガイド型オンボ(3経路ウェルカム)は廃止し、初回起動=軽い好み登録 →「あなたにおすすめの豆」→ 推薦から記録/送客に作り替える。Activation(KR1)は初回記録(推薦経由)を維持。3経路(抽出/撮影/手動)は常時使える記録手段として残す。
Description
新規ユーザーとして、初回起動で自分の好み(焙煎度・フレーバー・産地)をすばやく登録し、最初から「好みに合う・今買える豆」のおすすめを見て、そこから初日のうちに最初の1袋を記録 or 送客で出会えるようにしたい。
受け入れ条件
- 初回起動で、好みを 2-3タップ・スキップ可 で登録できる(入力は US-15 と同一・§3.5 #3)。
- 好み登録直後(スキップ時も)、ホーム「あなたにおすすめの豆」が表示される。未登録/カタログ薄でも人気・季節でフォールバックし空にしない(G-3)。
- おすすめカードから 記録(推薦経由・
catalog_bean_id紐付け)または送客 ができる(US-16)。 - 好みを使わず、3経路(ページ抽出/撮影/手動)で直接記録することもできる。
- 最初の1袋を保存すると、達成感のあるフィードバックが出る。
- (KR1)登録〜初日の初回記録到達を計測でき、推薦経由/直接を区別する(US-9連携)。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-8.1 | 初回起動=好み登録(2-3タップ・スキップ可)→ ホーム「あなたにおすすめの豆」着地(app_first_open発火・US-15 入力を再利用) | M(~2d) | US-15, US-16, US-9.1 | 1,2 |
US-8.2 | おすすめ経由の初回記録(推薦カード→記録/送客・catalog_bean_id アトリビューション)+達成フィードバック | M(~2d) | US-8.1, US-16 | 3,5 |
US-8.3 | 計測:登録〜初日初回記録到達を推薦経由/直接で区別(US-9.1 ファネル拡張:preference_set→recommend_view→bean_recorded) | S(~1d) | US-8.1, US-9.1 | 6 |
旧US-8(3経路ガイド型オンボで初回記録)から、好み先行・推薦経由の初回記録へ作り替え(2026-06-26)。重いオンボは廃止、3経路は常設の記録手段として存続。❓ Open Q:好みスキップ時の初回ホームの空状態コピー/推薦経由 vs 直接の達成FB差。
US-9:記録イベントを計測する(PM視点・PostHog) P0 / Must
Description
PMとして、記録のActivation・継続・所要時間・抽出品質を計測したい。OKR案Aの達成を数字で判断できるように。
受け入れ条件
- 「初回記録到達」イベントを、登録からの経過とともに記録する(Activation率)。
- ユーザー別の週次記録有無を集計できる(W1→W4継続=OMTM)。
- 記録1件あたりの所要時間を経路別(抽出/撮影/手動)に計測する。
- ページ抽出の項目自動充足率・誤り率・レイテンシを記録する。
- 月間記録豆数をユーザー別に集計できる。
- 計測は記録体験を遅くしない(非同期送信)。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-9.1 | Activation基盤(app_first_open→preference_set(任意)→recommend_view→bean_record_started→bean_recorded、24hファネル、推薦経由/直接の区別、非同期送信共通化) | M(~2d) | US-1 | 1,6 |
US-9.2 | 継続&ボリューム(週次リテンションW1→W4、月間記録豆数はPostgres真実源) | S(~1d) | US-9.1 | 2,5 |
US-9.3 | 所要時間計測(経路別 duration_ms、各フローに埋め込み) | M(~2d) | US-9.1 | 3 |
US-9.4 | 抽出品質計測(充足率/誤り率/レイテンシ、extraction_logs永続、誤り率はUS-3修正検知に依存) | M(~2d) | US-9.1, US-5 | 4 |
❓ Open Q:誤り率の「修正項目」確定窓(初回保存差分 or 後日編集も含むか)。
US-10:お気に入り店(対象店)を登録・編集・削除する P1 / Should
NOTE:回遊/キュレーションという発見体験はNEXT。ここは記録のための最小の店設定(店名+URL/ドメインの登録)に限定。v1ではPMが1〜2店をハードコードする運用でも代替可だが、ユースケースとして明示する。
Description
愛好家として、WebView閲覧とページ抽出の対象店を自分で登録・編集・削除したい。US-4で「登録した数店」を開ける前提を成立させ、買い回る店の変化に追従できるように。
受け入れ条件
- 店名と商品ページURL(またはドメイン)を指定して対象店を登録できる。
- 登録した店が一覧表示され、US-4のWebViewはこの一覧の店のみを開ける。
- 登録した店の名前・URLを編集でき、削除できる(削除後はその店URLが新規記録の抽出対象から外れる)。
- robots.txt等で抽出不可と判明した店には「撮影/手動推奨」フラグを表示する(US-6と連携)。
- 対象店が0件のときは、最初の店登録を促す空状態を表示する。
- (スコープ)対象は数店に限定。発見・回遊のためのブラウズ機能は持たない。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-10.1 | shopsテーブル定義(name/url/capture_flag/user_id)+RLS+マイグレーション | S(~1d) | — | 1,3 |
US-10.2 | 店登録フォーム(店名+URL/ドメイン正規化バリデーション→insert) | M(~2d) | US-10.1 | 1 |
US-10.3 | 登録店一覧(取得/表示/編集・削除導線、削除後は抽出対象外) | M(~2d) | US-10.1,2 | 2,3 |
US-10.4 | 店編集(プリフィル→update) | S(~1d) | US-10.3 | 3 |
US-10.5 | WebView許可リスト連携(onShouldStartLoadWithRequestで一覧ドメインのみ許可) | M(~2d) | US-10.3 | 2,6 |
US-10.6 | robots.txt 抽出不可判定→capture_flag+「撮影/手動推奨」フラグ(US-6連携) | M(~2d) | US-10.1,3 | 4 |
US-10.7 | 0件時の空状態UI(「最初の店を登録」CTA) | S(~1d) | US-10.3 | 5 |
❓ Open Q:URLとドメインどちらを正に(抽出キー)。capture_flagは自動のみ/手動上書き可か。
US-11:アカウント作成・ログイン・ログアウト P0 / Must
NOTE:認証は自前実装しない(PRD§7.3)。Supabase Auth等の外部認証をラップするUC。
Description
愛好家として、アカウントを作ってログインし、記録を自分のものとして保持したい。機種変更や複数端末でも同じコーヒー履歴書にアクセスできるように。
受け入れ条件
- 外部認証(メールリンク/OAuth等、自前パスワード保持なし)でアカウント作成・ログインできる。
- ログイン状態が永続化され、再起動後も維持される。
- ログアウトでき、ログアウト後はローカルの記録が他者に見えない状態になる。
- 認証アカウントと記録データが紐付き、複数端末から同一記録にアクセスできる土台になる。
- 認証の失敗・キャンセル時に明確なエラーを示し、記録機能の入口で詰まらせない。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-11.1 | 【スパイク】Supabase Auth 導入+方式決定(マジックリンク/OAuth)。Expo Deep Link・redirect URL を検証しADR化 | M(~2d) | — | 1 |
US-11.2 | 採用方式での実ログイン/サインアップ画面(signInWithOtp/signInWithOAuth 疎通) | M(~2d) | 11.1 | 1 |
US-11.3 | セッション永続化(AsyncStorage/SecureStore・autoRefresh・起動時復元・ルートガード) | M(~2d) | 11.2 | 2 |
US-11.4 | ログアウト(signOut+ローカルキャッシュクリア・他者非露出確認) | S(~1d) | 11.3 | 3 |
US-11.5 | 記録紐付け土台(beans.user_id+RLS本人のみCRUD・複数端末アクセス確認) | M(~2d) | 11.3 | 4 |
US-11.6 | 認証失敗/キャンセルのエラーUX(非ブロッキング・PostHog計測) | S(~1d) | 11.2 | 5 |
❓ Open Q:匿名先行利用(未ログインでローカル記録→後で紐付け)を認めるか(US-8の摩擦と連鎖)。NOWの認証方式は1つに絞れるか。
US-12:公開設定(visibility)の布石・既定private P1 / Should
NOTE:PRD§9.6が「NOWで先行追加する布石」と明記。NOWでは公開トグルUIの本格露出はせず、データとして保持・送信されることを保証するのが目的。
Description
PMとして、各豆記録にvisibility(private/public)フィールドを先行追加し、既定をprivateにしておきたい。NEXTの社会的発見(PRD§9)を、NOWのデータ構造の布石として安全に準備できるように。
受け入れ条件
- すべての豆記録が visibility(private/public)を持ち、既定は private。
- 既存記録・新規記録のいずれにも既定 private が確実に入る(マイグレーション含む)。
- NOWでは切り替えUIは最小または非露出でよいが、値はDBに保持されサーバーへ送信される。
- visibility=private の記録は、将来の社会的発見(§9)の集計対象に絶対に含まれないことが保証される。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-12.1 | beans.visibility(enum, NOT NULL DEFAULT 'private') 追加+既存行マイグレーション、型/Schema反映、UIは非露出/最小 | M(~2d) | US-11.5 | 1,2,3 |
US-12.2 | private除外の保証(公開用ビュー or RLSでpublic限定)+private混入しないリグレッションテスト | S(~1d) | 12.1 | 4 |
US-13:カメラ権限の要求と拒否時フォールバック P1 / Should
Description
愛好家として、袋撮影(US-7)の前にカメラ権限を求められ、拒否しても記録を諦めずに済むようにしたい。権限の有無で記録体験が壊れないように。
受け入れ条件
- 撮影記録の初回時にカメラ権限をリクエストする。
- 許可時は撮影に進み、拒否時は既存写真の選択 or 手動入力(US-1)へフォールバックする。
- 権限拒否後も、OS設定から再許可へ誘導する導線を示す。
- 権限状態にかかわらずアプリがクラッシュしない。
- (ガードレール)権限ダイアログ〜記録開始までユーザーを迷わせない。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-13.1 | 権限ステータス管理(useCameraPermissions・undetermined/granted/denied 中央管理) | M(~2d) | — | 1,4 |
US-13.2 | 許可時フロー(撮影画面US-7へ遷移) | S(~1d) | US-13.1 | 2 |
US-13.3 | 拒否時フォールバックUI(既存写真選択/手動入力US-1へ接続) | M(~2d) | US-13.1 | 2 |
US-13.4 | 再許可導線(Linking.openSettings()+復帰時の権限再チェック) | S(~1d) | US-13.1 | 3 |
US-13.5 | 事前説明(pre-permission priming)画面 | S(~1d) | US-13.1 | 5 |
US-13.6 | Maestro E2E(許可/拒否/設定復帰・クラッシュしない・計測) | M(~2d) | US-13.2,3,4 | 4,5 |
❓ Open Q:iOS/Androidの再要求挙動差の反映。写真選択のライブラリ権限拒否の二次フォールバック範囲。
US-14:オフライン記録→復帰後の抽出・同期 P2 / Could
NOTE:技術スパイク要:ローカルDB+同期戦略の検証が前提。見積前に調査が必要。
Description
愛好家として、電波のない店頭でも撮影・手動で記録を作れ、オンライン復帰時に自動で同期・抽出されるようにしたい。通信状況で記録を失わないように。
受け入れ条件
- オフライン時でも手動入力・袋撮影で記録を作成し、ローカルに保存できる。
- オンライン復帰時、未送信の記録がサーバーへ自動同期される。
- オフラインで作成した(URL付き)記録は、復帰後に
/crawl抽出が走りフォーム充填される。 - 同期で記録が重複・競合しない(記録は一意に保たれる)。
- 各記録の同期状態(未同期/同期済み)がユーザーに分かる。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-14.0 | 【スパイク】ローカルDB選定+同期戦略(UUID採番・冪等upsert・競合解決・NetInfo復帰検知) | L(~3d) | — | 1,2,4 |
US-14.1 | オフライン時の手動/撮影記録をローカルDBへ一次書込(UUID主キー) | M(~2d) | US-14.0 | 1 |
US-14.2 | 復帰検知→未送信記録をSupabaseへ自動同期(upsert冪等・同期キュー・リトライ) | L(~3d) | US-14.1 | 2,4 |
US-14.3 | 復帰後に source_url付き記録へ /crawl抽出を起動し充填(ポーリング) | M(~2d) | US-14.2 | 3 |
US-14.4 | 同期状態(未同期/同期中/済/失敗)をUIにバッジ表示 | S(~1d) | US-14.1 | 5 |
❓ Open Q:画像のオフライン保持と復帰後アップロード(US-7.2と整合)。同期失敗の再試行上限/通知。複数デバイスはMVP対象か。
US-15:好み(焙煎度・フレーバー・産地)を登録・編集する P1 / Should
NOTE:§3.5「コールドスタート好みレコメンド」の入力=本人の申告好み。初回起動でのキャプチャは US-8、推薦での消費は US-16 が担い、本ストーリーは user_preferences のデータ+設定からの編集/リセットに専念する。手動シードで NOW に前倒し済み(自動化のみ E7 ゲート後)。挽き方は好みに含めない(§3.5 #10)。
Description
愛好家として、記録がまだ無い初日でも「次の一杯」に出会えるよう、自分の好み(焙煎度・フレーバー・産地)を申告し、後から編集・リセットしたい。§3.5 のコールドスタート好みレコメンドが「本人の申告好み × 数店限定カタログ(catalog_beans)」を照合し、好みに合うけど未踏の豆をトップに提示できるように。
消費面:登録された好みは ホーム「あなたにおすすめの豆」(US-16)(好み一致を床に・未踏を上乗せ・在庫検証「今買える」)に反映される。これは §3.5 S4「トップページ面」の具体実体で、ホーム上の好みチップ(焙煎度/フレーバー)と推薦カードの理由文(例「浅煎り × フルーティ」「未踏の産地で冒険」)が user_preferences + catalog_beans のマッチング結果を可視化する。設計:Pencil「cappa Home」(Sec Recommend / component/BeanCard)。
受け入れ条件
- 初回起動(US-8)と設定画面の両方から、好みを2-3タップ・スキップ可・具体選択で登録できる(惹かれる焙煎度/フレーバー/産地を選ぶ・§3.5 #3)。
- 設定画面から、好みを後追いで登録/編集できる(初回でスキップした人も後から設定可能)。
- 好みは「焙煎度の好み」「フレーバー(tasting note)の好み」「産地の好み」で表現する(§3.5 #5 焙煎度床+フレーバー照合)。挽き方は含めない(§3.5 #10)。
- 好みをリセット(クリア)でき、リセット後の推薦は属性近傍ではなく人気/季節へ優雅にフォールバックする(G-3 / S4)。
- 好み未設定(スキップ)でも記録・閲覧は妨げない。推薦はフォールバックを出す(背骨は記録のまま・§3.5 #1)。
- 好みは本人のみ参照・編集できる(
user_preferencesの RLS 本人限定)。 - (計測)申告好み vs 実記録の属性一致率を計測できる土台を持つ(§3.5 #8 副シグナル・E7)。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-15.1 | user_preferences テーブル定義(user_id 1:1・roast_pref/flavor_pref/origin_pref・RLS本人限定)+マイグレーション(NOW) | S(~1d) | US-11 | 3,6 |
US-15.2 | 好み入力UI部品(2-3タップ・具体選択・スキップ・フォールバック)を US-8 初回と設定で共用 | M(~2d) | US-15.1 | 1 |
US-15.3 | 設定からの好み登録/編集/リセット画面 | M(~2d) | US-15.2 | 2,4 |
US-15.4 | 申告好み vs 実記録の属性一致率の計測土台(US-16と共通・US-9基盤・§3.5 #8) | S(~1d) | US-15.1, US-9 | 5,7 |
出典:レコメンド OST §3.5「コールドスタート好みレコメンド v1」(grilling 2026-06-21)。最大の生死リスク=「初回の申告好みが、愛好家が実際に動く豆を予測できる」(E7 の主検証)。崩れたら自動化に進まず、マッチングか入力 UX を直す。
US-16:ホーム「あなたにおすすめの豆」で好みに合う今買える豆と出会う P1 / Should NOW・手動シード(WoZ)
NOTE:§3.5「コールドスタート好みレコメンド」の常設面。社会的発見の共起(UC1-1/1-2)とは別軸で、他ユーザーのデータ密度(C2)を待たず本人の申告好み × 手動シードのカタログで動く。リリースは手動キュレーシード=そのまま E7(申告好み検証)の Wizard of Oz。自動化(カタログ自動クロール)は §3.5 #9 ゲート通過後=NEXT。
Description
愛好家として、毎回の起動でホーム「あなたにおすすめの豆」から、自分の好みに近く・今買える・まだ飲んでいない豆と出会いたい。好み一致を床に、未踏の発見を上乗せして、記録 or 送客に着地できるように。
受け入れ条件
- ホームに「あなたにおすすめの豆」枠を常設し、
user_preferences×catalog_beans(在庫検証済み「今買える」)を照合して提示する(§3.5 #5/#7)。 - 各カードは推薦理由(例「浅煎り × フルーティ」「未踏の産地で冒険」)と在庫バッジ(今買える/残りわずか)を示す(説明可能UX)。
- カードから記録(
catalog_bean_id紐付け)または送客(source_url)ができる。 - 好み未設定/カタログ薄では人気・季節へ優雅にフォールバックし、枠を空にしない(G-3)。挽き方はマッチングに使わない(§3.5 #10)。
- 好みチップ(焙煎度/フレーバー)で絞り込み・微調整できる(US-15 の好みと連動)。
- (計測)推薦→記録/送客の転換率、申告好み vs 実記録の属性一致率を計測(§3.5 #8・E7)。
実装サブタスク
| ID | 内容 | サイズ | 依存 | 元AC |
|---|---|---|---|---|
US-16.0 | 【運用】手動キュレーシード:数店から「今買える豆」を手選びし catalog_beans へ(在庫検証つき・§3.5 #6) | M(~2d) | — | 1 |
US-16.1 | マッチング v1:roast_pref/flavor_pref 床 + 未踏(未記録)上乗せ + 在庫フィルタ(§3.5 #5/#7/#2) | M(~2d) | US-15.1, US-16.0 | 1,4 |
US-16.2 | ホーム「あなたにおすすめの豆」UI(推薦カード・理由文・在庫バッジ・好みチップ・空状態フォールバック) | M(~2d) | US-16.1 | 2,4,5 |
US-16.3 | 推薦→記録/送客(catalog_bean_id アトリビューション)+転換率・一致率の計測(US-9基盤・§3.5 #8/E7) | M(~2d) | US-16.2, US-9 | 3,6 |
設計面:Pencil「cappa Home」Sec Recommend / component/BeanCard。北極星「出会い数」を守るため、推薦は必ず「記録 or 送客」に着地させる(§3.5 #4)。自動化は §3.5 #9 自動化GOゲート(転換率≥30% / n≥20-30 / 一致率OK)通過後=NEXT。
非機能要件・ガードレール
| カテゴリ | 要件 | 目標 | 優先 |
|---|---|---|---|
| パフォーマンス | 手動記録(必須のみ)の所要時間 | ≤ 30秒 | Must |
| パフォーマンス | 撮影記録の所要時間 | ≤ 60秒 | Must |
| パフォーマンス | ページ抽出 取り込み〜充填レイテンシ | ≤ 10秒 | Must |
| 抽出品質 | ページ抽出の項目自動充足率 | ≥ 80% | Must |
| 抽出品質 | 抽出の誤り率(保存時修正をプロキシ) | ≤ 10% | Should |
| 認証 | 認証を自前実装しない(Supabase Authに委譲) | 匿名→リンクのみ実装 | Must |
| コスト | MVP規模の月額ランニング | ≒ $5/月 | Must |
| 型安全 | TS strict+全境界Zod、any禁止、生成型をCI再生成 | tsc/lintをCIゲート | Must |
| テスト | テストピラミッド+E2E(Maestro)でFlow A/B/C+失敗系 | CIで自動実行 | Should |
| 運用 | OTA(EAS Update)でストア審査を待たず修正配信 | JS層は即時配信 | Could |
出典:技術選定 ノート §2/§7/§8/§9/§10 / OKRガードレール(保持・統合)
システム / アーキテクチャ
推奨スタックは Expo(React Native) × Supabase × Cloudflare /crawl × PostHog × Maestro。ベンダーを4社に抑え、認証・計測の自作を避ける。機微トークンは Edge Function 内のみに置きクライアントへ出さない。
楽観更新・ポーリング] PH1[PostHog SDK] end subgraph SB["Supabase (マネージド)"] AUTH[Auth
匿名→リンク] DB[(Postgres + RLS)] EF1[Edge Fn: extract-bean] EF2[Edge Fn: extract-photo] ST[Storage 袋写真] RT[Realtime] end CF[Cloudflare /crawl
AI構造化抽出] VIS[Vision LLM] PH[(PostHog
ファネル/リテンション)] WV -- 現在URL --> UI --> Q Q <-->|typed client| DB Q -->|URL| EF1 -->|JSON Schema| CF UI -->|画像| EF2 --> VIS ST --- EF2 AUTH --- DB DB -. Realtime更新 .-> Q PH1 --> PH style SB fill:#3ecf8e,color:#000 style CF fill:#f6821f,color:#fff style Client fill:#e7ecff,color:#000
技術スタック(要点)
| レイヤー | 採用 | 主因 |
|---|---|---|
| アプリ基盤 | Expo (React Native) + EAS | 運用工数最小・OTA更新 |
| 言語/型 | TypeScript strict + Zod | 全境界で実行時検証+型推論 |
| サーバ状態 | TanStack Query | 楽観更新・抽出ポーリングに最適 |
| BaaS/認証/DB | Supabase(Auth/Postgres/RLS/Edge/Storage/Realtime) | 認証委譲・1ベンダー集約 |
| ページ抽出 | Edge Function → Cloudflare /crawl | トークン秘匿・サーバ集約 |
| 計測 | PostHog(+ Postgres SQL) | リテンション/ファネルが標準装備 |
| E2E | Maestro | YAML・Expo相性・CI容易 |
要石スキーマ(1スキーマ3役)
PRD §7.3 の「1スキーマ3役」を共有 Zod パッケージで物理的に一元化する。これが抽出・記録・将来レコメンドを貫く単一ソース。
coffeeBeanSchema (Zod)"] --> T["TS型 z.infer
(アプリ&Edge共通)"] Z --> J["JSON Schema
(/crawl response_format)"] Z --> F["RHF zodResolver
(フォーム検証)"] Z -. 整合 .-> D["Postgres beans表
(gen types)"] style Z fill:#e7ecff,color:#000
データモデル:豆記録(beans)の主なフィールド
| フィールド | 型 | 必須 | 備考 |
|---|---|---|---|
name | string | ◯ | 銘柄名 |
shop | string | ◯ | 購入元(店名) |
source_url | string(url) | – | 商品ページURL(再注文導線) |
origin_country / origin_region | string | – | 生産国 / 地域・農園・精製所 |
variety | string | – | 品種(例:ゲイシャ) |
process | enum | – | washed / natural / honey / anaerobic / other |
roast_level | enum | – | light 〜 dark |
roast_date / purchased_at | date | purchased_at ◯ | 焙煎日 / 購入日(既定=当日) |
weight_g / price_jpy | number | – | 内容量(g) / 価格 |
tasting_notes / my_memo | string | – | 風味メモ / 自由メモ |
image_url | string | – | 袋写真 / 商品画像 |
付随テーブル:extraction_logs(充足率・誤り率・レイテンシ・shop=OKRの抽出ガードレール用)。beans には status: 'extracting'|'ready'、source: 'web'|'photo'|'manual'。RLS は beans.user_id = auth.uid() のみ許可。
認証(自前実装しない)
出典:技術選定 ノート §3/§5/§6(保持・要約)
エンティティ一覧(ER図)
PRD §7.3 の豆スキーマと全14ユーザーストーリー(US-1〜14)の永続要件から導いた PostgreSQL(Supabase) のデータモデル。9. システム / アーキテクチャ で触れた beans / extraction_logs を正式化し、users / shops / extraction_jobs を補って NOWスコープを漏れなくカバーする。社会的発見(§10・PRD §9)は条件付きNEXTのため、布石となる beans.visibility のみ含め、共起集約ストアは将来テーブルとして末尾に注記する。コールドスタート好みレコメンド(§3.5・US-16)の推薦元カタログは、GitHub #175 の自動収集パイプライン(catalog_sources → catalog_items → catalog_beans)に更新済み。
catalog_beans は §3.5(コールドスタート好みレコメンド・US-16)の入力で、catalog_sources(巡回対象)→ catalog_items(内部台帳)→ catalog_beans(表示用)の自動収集パイプライン(discover/harvest/refresh・GitHub #175)で生成される。beans への推薦アトリビューション(catalog_bean_id)はNEXT。catalog_state はCF cooldownを保持する単一行の運用テーブルでFK関係を持たない。列挙型(ENUM)の定義
| 型 | 値 | 使用箇所 / 根拠 |
|---|---|---|
process_type | washed / natural / honey / anaerobic / other | beans.process(PRD §7.3) |
roast_level | light / medium-light / medium / medium-dark / dark | beans.roast_level(PRD §7.3) |
visibility | private / public | beans.visibility(US-12・既定private) |
bean_status | extracting / ready | beans.status(US-5 楽観更新の"解析中") |
bean_source | web / photo / manual | beans.source / extraction_logs.route(US-9.3 経路別計測) |
extraction_kind | page / photo | extraction_jobs.kind(US-5=ページ、US-7=画像で別系統) |
extraction_status | processing / done / failed | extraction_jobs.status(US-5.2 非同期ポーリング) |
shop_capture | extractable / manual_recommended / unknown | shops.capture_flag(US-10.6 robots判定) |
catalog_adapter | shopify_js / jsonld / none | catalog_sources.adapter(§4.2・GitHub #175) |
catalog_item_status | pending / processing / ready / rejected / delisted / failed | catalog_items.status(§7・GitHub #175) |
users(アカウント) US-11
Supabase Auth が auth.users を管理し、本テーブルはその公開プロファイル(1:1)。自前パスワードは持たない。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | — | PK・FK → auth.users(id) ON DELETE CASCADE |
email | text | NOT NULL | — | UNIQUE(auth 由来) |
display_name | text | NULL | — | 表示名(任意) |
created_at | timestamptz | NOT NULL | now() | 作成日時 |
shops(対象店) US-10
WebView閲覧(US-4)とページ抽出の対象店レジストリ。回遊・キュレーションは持たない最小設定。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
user_id | uuid | NOT NULL | — | FK → users(id) ON DELETE CASCADE |
name | text | NOT NULL | — | 店名(US-10.1) |
url | text | NOT NULL | — | 商品ページURL(US-10.2) |
domain | text | NOT NULL | — | 正規化ホスト。WebViewホワイトリスト照合(US-10.5) |
capture_flag | shop_capture | NOT NULL | 'unknown' | robots判定で抽出可否(US-10.6 / US-6連携) |
created_at | timestamptz | NOT NULL | now() | |
updated_at | timestamptz | NOT NULL | now() | 編集時更新(US-10.4) |
UNIQUE(user_id, domain)で同一ユーザーの重複登録を防止。
beans(豆記録・中核) US-1
要石スキーマ(PRD §7.3)の物理実体。必須は name / shop / purchased_at の3項目のみ(US-1 AC1)。残りは空(NULL)で保存可=摩擦最小。id はクライアント採番UUIDでオフライン冪等upsert(US-14)に対応。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK。クライアント採番可(US-14 同期の一意性) |
user_id | uuid | NOT NULL | — | FK → users(id) ON DELETE CASCADE。RLS本人のみ(US-11.5) |
shop_id | uuid | NULL | — | FK → shops(id) ON DELETE SET NULL。撮影/手動記録は未設定可 |
name | text | NOT NULL | — | 銘柄名(必須) |
shop | text | NOT NULL | — | 購入元店名(必須・非正規化/JSON Schema契約) |
source_url | text | NULL | — | 商品ページURL(再注文導線・US-2.3) |
origin_country | text | NULL | — | 生産国 |
origin_region | text | NULL | — | 地域 / 農園 / 精製所 |
variety | text | NULL | — | 品種(例:ゲイシャ) |
process | process_type | NULL | — | 精製方法(enum) |
roast_level | roast_level | NULL | — | 焙煎度(enum) |
roast_date | date | NULL | — | 焙煎日 |
weight_g | integer | NULL | — | 内容量(g)。CHECK(weight_g > 0) |
price_jpy | integer | NULL | — | 価格(円)。CHECK(price_jpy >= 0) |
purchased_at | date | NOT NULL | CURRENT_DATE | 購入日(必須・既定=当日/US-1 AC4) |
tasting_notes | text | NULL | — | 風味メモ |
my_memo | text | NULL | — | 自由メモ |
image_url | text | NULL | — | 袋写真 / 商品画像(US-7.2 Storage) |
visibility | visibility | NOT NULL | 'private' | 公開設定の布石(US-12・既定非公開を保証) |
status | bean_status | NOT NULL | 'ready' | 抽出中=extracting(US-5 楽観更新) |
source | bean_source | NOT NULL | 'manual' | 記録経路(web/photo/manual・US-9.3) |
catalog_bean_id | uuid | NULL | — | FK → catalog_beans(id) ON DELETE SET NULL。推薦経由の記録アトリビューション(§3.5・US-16)。NULL=通常経路 |
created_at | timestamptz | NOT NULL | now() | 一覧の新着順(US-2.1) |
updated_at | timestamptz | NOT NULL | now() | 編集で更新(US-3) |
extraction_jobs(抽出ジョブ) US-5 US-7
「記録を先に作る→裏で抽出」の非同期ジョブ(US-5.3)。ページ抽出(Cloudflare /crawl)と画像抽出(Vision LLM)を kind で1テーブルに統合。1豆が複数回の抽出を持ちうる(失敗→再試行)。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
bean_id | uuid | NOT NULL | — | FK → beans(id) ON DELETE CASCADE |
kind | extraction_kind | NOT NULL | — | page=/crawl / photo=Vision(別系統) |
input_url | text | NULL | — | page 時の対象URL |
input_image_url | text | NULL | — | photo 時の画像 |
external_job_id | text | NULL | — | Cloudflare /crawl の jobId(ポーリング用) |
status | extraction_status | NOT NULL | 'processing' | processing / done / failed(US-5.2) |
result | jsonb | NULL | — | 抽出された構造化JSON(記載なしはnull固定・推測禁止) |
error_code | text | NULL | — | 失敗理由(robots拒否 / timeout 等・US-6) |
started_at | timestamptz | NOT NULL | now() | ジョブ起動時刻 |
finished_at | timestamptz | NULL | — | 完了 / 失敗時刻 |
latency_ms | integer | NULL | — | 所要時間(10秒ガードレール監視・US-5.7) |
extraction_logs(抽出品質ログ) US-9
抽出ガードレール(充足率80% / 誤り率 / レイテンシ)のPostgres真実源(US-9.4)。行動ファネル(Activation・リテンション)は PostHog(外部)で集計し、本テーブルは抽出品質の集計軸に専念する。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
user_id | uuid | NOT NULL | — | FK → users(id) ON DELETE CASCADE |
bean_id | uuid | NULL | — | FK → beans(id) ON DELETE SET NULL |
extraction_job_id | uuid | NULL | — | FK → extraction_jobs(id) ON DELETE SET NULL |
shop | text | NULL | — | 店別集計軸(既存システム設計に準拠) |
route | bean_source | NOT NULL | — | 経路別計測(web/photo/manual・US-9.3) |
fields_total | smallint | NOT NULL | — | スキーマ項目数(充足率の分母) |
fields_filled | smallint | NOT NULL | 0 | 自動充填できた項目数 |
fields_corrected | smallint | NOT NULL | 0 | ユーザー修正数(誤り率の分子) |
fill_rate | numeric(4,3) | NULL | 生成列 | GENERATED:fields_filled / NULLIF(fields_total,0) |
latency_ms | integer | NULL | — | 抽出レイテンシ |
created_at | timestamptz | NOT NULL | now() | 計測時刻 |
catalog_sources(巡回対象ショップ) NOW・#175
カタログ自動収集パイプライン(discover / harvest / refresh)が巡回する数店のショップレジストリ。ユーザーが登録するWebView対象店 shops とは無関係で、クライアントからは一切見えない(RLS有効・ポリシー無し・GRANT無し。書き込みはservice_roleのみ)。product_url_pattern は商品URL判定用の正規表現(Deno側で評価)で、catalog:probe CLI(#188)が提案し人間が承認してmigrationでseedする。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
name | text | NOT NULL | — | 店名(catalog_beans.shop_name に書き写す) |
site_url | text | NOT NULL | — | サイトルート |
sitemap_url | text | NOT NULL | — | discover が読むsitemap |
product_url_pattern | text | NOT NULL | — | 商品URL判定用RegExp(Postgresでは使わない) |
adapter | catalog_adapter | NOT NULL | 'none' | harvest の抽出方式 |
enabled | boolean | NOT NULL | false | 巡回対象に含めるか |
last_discovered_at | timestamptz | NULL | — | 直近のdiscover実行時刻 |
last_discovered_count | integer | NULL | — | 直近の発見件数。急減でパターン腐敗に気付く手掛かり |
last_error | text | NULL | — | 直近のdiscoverエラー |
created_at | timestamptz | NOT NULL | now() | |
updated_at | timestamptz | NOT NULL | now() |
POST COFFEE / LIGHT UP COFFEE / LILO COFFEE ROASTERS / 森彦珈琲 の4店を enabled = true でseed済み。
catalog_items(クローラ内部台帳) NOW・#175
discover が発見した商品URLの状態機械。失敗状態・試行回数を持つためクライアントには一切公開しない(RLS有効・ポリシー無し・GRANT無し)。catalog_beans は本テーブルのready到達行から生成される表示専用の写像。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
source_id | uuid | NOT NULL | — | FK → catalog_sources(id) ON DELETE CASCADE |
url | text | NOT NULL | — | sitemap上のURL(生値) |
canonical_key | text | NOT NULL | — | UNIQUE。catalogKey(url)(§6.5)で正規化した重複キー |
status | catalog_item_status | NOT NULL | 'pending' | discover〜harvestの状態 |
reject_reason | text | NULL | — | not_a_bean / no_name 等 |
attempts | integer | NOT NULL | 0 | harvest再試行回数 |
last_error | text | NULL | — | 直近のharvestエラー |
claimed_at | timestamptz | NULL | — | processing遷移時刻。10分超過で失効扱い(atomic claim) |
first_seen_at | timestamptz | NOT NULL | now() | 台帳への初回登録時刻。claimの巡回順に使用 |
last_seen_in_sitemap_at | timestamptz | NOT NULL | now() | 直近のsitemapで確認できた時刻。delisted判定に使用 |
created_at | timestamptz | NOT NULL | now() | |
updated_at | timestamptz | NOT NULL | now() |
claim_catalog_items(batch_size) 関数が for update skip locked で毎分cronと手動kickの重複取得を防ぐ(service_roleのみEXECUTE)。
catalog_beans(推薦元カタログ・表示用) NOW・自動収集
コールドスタート好みレコメンド(§3.5「明示好み×カタログ」・US-16)の推薦元。GitHub #175 の自動収集パイプラインが生成する:discover(毎日・sitemapから商品URLを発見してcatalog_itemsへ)→ harvest(毎分・無料ゲート通過分をCloudflare抽出+LLMタグ化して本テーブルへupsert)→ refresh(毎日・在庫と店名を再確認)。1行はcatalog_itemsの1行に対応する(item_id UNIQUE FK)。クローラの失敗状態やrejected行を持つ台帳とは別エンティティに分離し、RLSを「全員読み取り専用」の1文で書けるようにする。ユーザー個人の記録 beans とは所有者(システム vs ユーザー)・ライフサイクル(定期更新の生鮮 vs 不変の記録)・公開性(公開 vs private)が異なるため別エンティティのまま。属性コア(process/roast_level/tasting_notes 等)は共有 Zod で beans と再利用する。RLS は「全員読み取り可・書き込みはservice_roleのみ(ポリシー無し)」(beans の本人限定とは逆)。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | uuid | NOT NULL | gen_random_uuid() | PK |
item_id | uuid | NOT NULL | — | FK → catalog_items(id) ON DELETE CASCADE。UNIQUE(1台帳行につき最大1掲載) |
shop_name | text | NOT NULL | — | 店名。catalog_sources.name を書き写した非正規化列(表示のたびにjoinしない) |
source_url | text | NOT NULL | — | 商品ページURL(送客先) |
name | text | NOT NULL | — | 銘柄名。カタログ内で唯一の必須項目 |
origin_country | text | NULL | — | 生産国 |
origin_region | text | NULL | — | 地域 / 農園 / 精製所 |
variety | text | NULL | — | 品種 |
process | process_type | NULL | — | 精製方法(beans と同一ENUM) |
roast_level | roast_level | NULL | — | 焙煎度(beans と同一ENUM) |
roast_date | date | NULL | — | 焙煎日 |
weight | integer | NULL | — | 内容量(g)。CHECK(weight > 0) |
price | integer | NULL | — | 価格(円)。CHECK(price >= 0) |
tasting_notes | text | NULL | — | 風味メモ(表示用の自由文) |
image_url | text | NULL | — | 商品画像URL。不正/相対URLでも行は落とさずclient側でnull化 |
flavor_tags | text[] | NOT NULL | '{}' | フレーバータグ最大3件。CHECKは長さのみ・値域はDBで縛らない(zodで検証・spec D11)。user_preferences.flavor_pref と集合演算で照合 |
origin_key | text | NULL | — | 産地の正規化キー。user_preferences.origin_pref と照合 |
available | boolean | NOT NULL | true | 在庫フラグ(一期一会フィルタ・§3.5 #2)。refresh が更新 |
last_checked_at | timestamptz | NULL | — | refresh が最後に在庫確認した時刻 |
created_at | timestamptz | NOT NULL | now() | おすすめの新着順の正本(#198) |
updated_at | timestamptz | NOT NULL | now() | 「最後に何か書いた時刻」。refreshが無変更行にも書くため内容変更時刻ではなく、クライアントは読まない(#198) |
UNIQUE(item_id)で1台帳行につき最大1掲載に制約(重複クロール自体の防止は catalog_items.canonical_key UNIQUE が担う・§6.5)。index catalog_beans_available_created_at_idx(available, created_at desc)がおすすめ取得を支える。beans.catalog_bean_id(任意FK・NEXT)が「この推薦経由で記録された」アトリビューションを張る想定で、§3.5 #8(推薦→記録の転換率)の計測軸になる。将来の bean_cooccurrences(社会的発見の派生集計)とも別物。
catalog_state(CFレート制御・運用状態) NOW・#175
Cloudflare Browser Renderingのレート制限はアカウント単位でソース横断のため、cooldownはソースごとの行ではなく常に1行だけの単一行テーブルに置く。harvestは開始時にcf_cooldown_untilを見て、未来なら何もせず終了する。他エンティティとのFK関係を持たない運用テーブル。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
id | boolean | NOT NULL | true | PK。CHECK(id)で常に1行のみ許可 |
cf_cooldown_until | timestamptz | NULL | — | この時刻までharvestのCF呼び出しを止める |
cf_cooldown_seconds | integer | NOT NULL | 600 | 次に429を受けたときの待ち時間(指数バックオフの現在値) |
updated_at | timestamptz | NOT NULL | now() |
user_preferences(申告好み) §3.5・NOW
コールドスタート好みレコメンド(§3.5「明示好み×カタログ」)の入力。ユーザーが初回オンボ/設定で申告する好みを 1ユーザー1行で保持し、catalog_beans(推薦元・店所有)と照合して「好み一致を床に、未踏の豆を上乗せ」する(§3.5 #5/#7)。挽き方は持たない(§3.5 #10)。RLS は beans と同じく本人のみ。
| 列 | 型 | NULL | デフォルト | キー / 制約・説明 |
|---|---|---|---|---|
user_id | uuid | NOT NULL | — | PK・FK → users(id) ON DELETE CASCADE。1ユーザー1行(1:1)・RLS本人のみ |
roast_pref | roast_level[] | NULL | — | 焙煎度の好み(複数可・beans と同一ENUM)。フレーバー床(§3.5 #5) |
flavor_pref | text[] | NULL | — | フレーバーの好みタグ。catalog_beans.flavor_tags と集合演算で照合(§8.2) |
origin_pref | text[] | NULL | — | 産地(国/地域)の好み(複数可)。catalog_beans.origin_key と照合 |
created_at | timestamptz | NOT NULL | now() | |
updated_at | timestamptz | NOT NULL | now() | 編集・リセットで更新(US-15.3) |
全列 NULL 可=スキップ/未設定を許容(§3.5 #3)。未設定時はコールドスタート推薦が人気/季節へフォールバック(G-3)。flavor_pref の物理表現(text[] かタグ正規化テーブル)は実装時の設計余地。挽き方は持たない(§3.5 #10)。
リレーション一覧
| 親 | 子 | 多重度 | FK列 | ON DELETE | 根拠 |
|---|---|---|---|---|---|
users | shops | 1 — 0..N | shops.user_id | CASCADE | US-10 |
users | beans | 1 — 0..N | beans.user_id | CASCADE | US-11.5 |
users | extraction_logs | 1 — 0..N | extraction_logs.user_id | CASCADE | US-9 |
shops | beans | 0..1 — 0..N | beans.shop_id | SET NULL | US-4 / US-1(任意) |
beans | extraction_jobs | 1 — 0..N | extraction_jobs.bean_id | CASCADE | US-5 / US-7 |
beans | extraction_logs | 0..1 — 0..N | extraction_logs.bean_id | SET NULL | US-9.4 |
extraction_jobs | extraction_logs | 0..1 — 0..N | extraction_logs.extraction_job_id | SET NULL | US-9.4 |
catalog_sources | catalog_items | 1 — 0..N | catalog_items.source_id | CASCADE | discover(§5・GitHub #175) |
catalog_items | catalog_beans | 1 — 0..1 | catalog_beans.item_id | CASCADE | harvest(§5・GitHub #175) |
catalog_beans | beans | 0..1 — 0..N | beans.catalog_bean_id | SET NULL | §3.5(推薦→記録アトリビューション・US-16/NEXT) |
users | user_preferences | 1 — 0..1 | user_preferences.user_id | CASCADE | §3.5(US-15 申告好み・NOW) |
ユースケース → エンティティ対応(網羅性チェック)
| US | 要件 | 関与エンティティ / 列 |
|---|---|---|
| US-1 | 手動記録(必須3項目+任意) | beans 全列 |
| US-2 | 一覧・詳細(新着順・再注文) | beans.created_at / source_url / image_url |
| US-3 | 編集・削除 | beans.updated_at / 行削除 |
| US-4 | WebView(対象店のみ) | shops.domain |
| US-5 | ページ抽出(主経路・楽観更新) | extraction_jobs(kind=page)/ beans.status |
| US-6 | 抽出失敗→撮影/手動退避 | extraction_jobs.status,error_code / shops.capture_flag |
| US-7 | 袋撮影で充填 | extraction_jobs(kind=photo)/ beans.image_url |
| US-8 | オンボーディング(初日1袋) | beans(最初の行)※ファネルはPostHog |
| US-9 | 計測(抽出品質) | extraction_logs / 行動はPostHog |
| US-10 | 対象店の登録・編集・削除 | shops 全列 |
| US-11 | 認証・複数端末 | users / 各表 user_id + RLS |
| US-12 | visibility布石(既定private) | beans.visibility |
| US-13 | カメラ権限(端末側) | 永続データなし(OS権限・クライアント状態) |
| US-14 | オフライン記録→同期 | beans.id(UUID冪等upsert)※端末ローカルDBは別レイヤ |
| US-15 | 好みの登録・編集(コールドスタート好みの入力・NOW) | user_preferences 全列 |
| US-16 | ホーム「あなたにおすすめの豆」(好み×カタログ照合・NOW自動収集) | user_preferences × catalog_beans(rankCatalogBeans) / beans.catalog_bean_id(推薦アトリビューション・NEXT) |
- 社会的発見の共起集約ストア(PRD §9.6):
visibility='public'のbeansから item-item 共起をバッチ生成する集計テーブル(例bean_cooccurrences:bean_a / bean_b / co_count、k-匿名性 n≥5)。検証ゲートE1/E3通過後に追加するため本ER図には含めない。NOWではbeans.visibilityの布石のみ用意。 - 端末ローカルDB(US-14:WatermelonDB/expo-sqlite):オフライン一次書込みと同期状態(未同期/同期中/済/失敗)はクライアント側に存在し、Postgres には冪等upsertで反映される。サーバ側ERの対象外。
出典:PRD §7.3 豆スキーマ / ユーザーストーリー US-1〜14(実装サブタスク含む)/ システム/アーキテクチャ §9(beans・extraction_logs を正式化)。設計判断(shop_id任意リンク・抽出ジョブ統合・生成列fill_rate)は2026-06-21に補完。
社会的発見ディスカバリー NEXT・条件付き
「他ユーザーの記録を閲覧して出会いを増やす」アイデアの検証計画。回遊+レコメンドだけでは出会い数が頭打ちになる、という仮説に対する第3の発見エンジン。フォロー/フィードを持たない匿名共起で §5「SNS化しない」と両立を狙う。NOWには混ぜず NEXT 条件付き。
検証に進める3案
| # | アイデア | 要点 |
|---|---|---|
| 1 | item-based「この豆が好きな人は次にこれ」 | 豆単位の共起。ユーザーが少なくても成立・SNS要素ゼロ |
| 2 | テイスト・ツイン発見(匿名) | 嗜好近傍ユーザーの高評価豆を匿名提示 |
| 3 | オプトイン公開+visibilityスキーマ内蔵 | 機能でなく設計判断。低コストで将来の選択肢を確保 |
クリティカル前提(Leap of Faith)
| ID | 前提 | 状態 |
|---|---|---|
C2 | 記録密度・ユーザー間の重なりが社会的シグナルに足りる(全案の生命線) | 最大の前提 |
#1-購入 | 共起した豆が一期一会で今も購入可能 | 盲点級 |
C1 | そもそも出会い数は頭打ちになる(需要の存在証明) | 未検証 |
C3 | 他者由来の発見はアルゴ単独より刺さる | 未検証 |
検証実験(4実験で12前提を回収)
| 実験 | 手法 | 成功しきい値 |
|---|---|---|
E1 シードデータ監査 | 既存記録のデータ分析(机上) | 重なり率≥15% / 共起ペア≥30 / 購入可能率≥40% |
E2 愛好家インタビュー | 過去行動を聞く(困難時は公開コミュニティ観察へ) | 人由来発見の実例≥3/5 / 公開前向き≥3/5 |
E3 コンシェルジュMVP | Wizard of Oz(手動推薦) | 購入意向がベースライン比+30% / 送客クリック≥1/人 |
E4 visibilityスキーマ内蔵 | 技術タスク(UI非表示の布石) | OMTMに悪影響なく将来をマイグレ無しで開ける |
生死判定ゲート"] E1 -->|C2/購入可能性 OK| E3["E3 コンシェルジュMVP
価値の実証"] E1 -->|C2 NG| K["社会的発見を凍結
NEXT後半へ"] E3 -->|C3実証| X["NEXTの発見エンジンに正式採用"] E3 -->|C3棄却| K2["§5維持・打ち切り"] style E1 fill:#e7ecff,color:#000 style E3 fill:#635bff,color:#fff
出典:ディスカバリープラン - 社会的発見 ノート(要約)
ユースケース(社会的発見) NEXT・設計段階
社会的発見ディスカバリーの3案(#1 共起 / #2 ツイン / #3 公開)を、シナリオ型ユースケースに落とし込んだもの。未検証の設計段階であり、着手はディスカバリーの検証ゲート(E1→E3)通過が前提。各UCは依存する前提〔C1〜C3〕・実験〔E1〜E4〕と、共通の横断ガード(G-1〜G-5)に紐づく。
アクターと凡例
| アクター | 説明 |
|---|---|
| 愛好家(利用者) | 発見の受け手。状態で2区分:①データ希薄(共起/近傍が出にくい)/②データ充実 |
| 公開提供者 | 同一人物の別ロール。自分の記録を匿名で共有プールに供給する側(#3) |
| 発見システム | 匿名共起の集計・テイスト近傍計算・visibility/匿名性フィルタを担う裏方 |
UCは アクター / モーメント / 依存〔前提・実験〕 / 事前条件 / トリガー / 基本フロー / 代替・例外 / 事後条件 で記述。例外の多く(空状態・k-匿名性・一期一会・SNS化回避・プライバシー)は横断ガード章(G-x)に集約し参照する。
UC一覧
| ID | ユースケース | 案 | モーメント |
|---|---|---|---|
UC1-1 | 豆詳細で「この豆が好きな人は次にこれ」から次の豆に出会う | #1 共起 | 豆詳細を見ている時 |
UC1-2 | 記録直後に関連する共起豆をサジェストされる | #1 共起 | 記録した直後 |
UC2-1 | 好みが近い人(匿名)の高評価豆から出会う | #2 ツイン | 次に何を買うか迷う時 |
UC3-1 | 自分の記録を匿名で「発見プールに提供」する(オプトイン) | #3 公開 | 価値を実感した後 |
UC3-2 | 記録単位で公開/非公開を切り替える | #3 公開 | 記録の見直し時 |
UC3-3 | 公開を撤回する | #3 公開 | 気が変わった時 |
UC2-2「テイスト・ツインの新着で再訪(通知)」は、選定3案外+SNS化/匿名性リスクのため今回スコープ外。将来「個人を出さない集約通知(あなたの好み圏で人気上昇中の豆)」として再検討する。
出典:ディスカバリープラン - 社会的発見 を起点に、ブレインストーミング(2026-06-21)で新規設計
UC1-1:豆詳細で「この豆が好きな人は次にこれ」から次の豆に出会う NEXT・設計段階
| アクター/モーメント | 愛好家・発見システム / 豆詳細を見ている時 |
| 依存〔前提/実験〕 | C2・C3・#1購入 / E1・E3 |
| 事前条件 | 対象豆に k-匿名性を満たす匿名共起データが存在(G-2) |
| トリガー | 利用者が豆詳細を開く |
基本フロー
- 利用者が豆詳細を開く
- システムが「この豆を記録した人が他に記録した豆」を集計取得 → k-匿名性フィルタ+購入可能性で再ランク(G-2・G-3)
- 「この豆が好きな人は次にこれ」枠に上位N件を表示(出所は「みんなの記録から」=個人名なし/G-4)
- 利用者が候補をタップ → 候補豆の詳細
- 気に入れば記録、またはショップ商品ページへ(送客)
代替・例外
- 代替 3a:購入可能な候補が乏しい → プロファイル共起(産地/焙煎度が近い豆)に切替(G-3)
- 例外 2a:共起がk-匿名性未満/データ不足 → 枠を出さずアルゴ・回遊にフォールバック(G-1)
事後条件:新規記録(=出会い)または送客クリック。共起経由の新規記録率を計測。
UC1-2:記録直後に関連する共起豆をサジェストされる NEXT・設計段階
| アクター/モーメント | 愛好家・発見システム / 記録した直後 |
| 依存〔前提/実験〕 | C2・C3 / E1・E3 |
| 事前条件 | 記録が保存完了し、その豆にk-匿名性を満たす共起がある |
| トリガー | 記録の保存完了(楽観更新の確定後) |
基本フロー
- 記録完了画面を表示
- 「この豆が好きな人は次にこれ」を1〜数件だけ提示(G-2・G-3適用)
- タップ → 候補詳細 → 記録 or 送客
代替・例外
- 例外 2a:データ不足 → 何も出さない(記録直後の摩擦を増やさない=OMTM保護/G-1)
事後条件:連続的な発見、出会い数の増加。
UC2-1:好みが近い人(匿名)の高評価豆から出会う NEXT・設計段階
| アクター/モーメント | 愛好家(データ充実)・発見システム / 次に何を買うか迷う時 |
| 依存〔前提/実験〕 | C2・C3・#2「刺さる」・#2「少データ近傍」 / E1・E3 |
| 事前条件 | 嗜好ベクトルが算出できる最小記録数を満たす/近傍ユーザー群がk-匿名性を満たす(G-2) |
| トリガー | 「あなたへの発見(好みが近い人)」枠を開く |
基本フロー
- 利用者が発見枠を開く
- システムが嗜好ベクトル近傍(テイスト・ツイン)をk人以上集約 → 高評価豆を匿名集計
- 「好みが近い人がよかった豆」を購入可能性で再ランクし提示(個人プロフィール/名前なし・嗜好グラフのみ/G-4)
- タップ → 詳細 → 記録 or 送客
代替・例外
- 代替 3a:購入可能候補が乏しい → プロファイル軸(産地/焙煎度傾向)のおすすめに切替(G-3)
- 例外 2a:記録が浅く近傍が定まらない → 「もう少し記録すると精度が上がる」空状態+共起/アルゴへフォールバック(G-1)
- 例外 2b:近傍がk-匿名性未満 → 出さない(G-2)
事後条件:出会い(新規記録)・送客。ツイン経由の出会い率を計測。
UC3-1:自分の記録を匿名で「発見プールに提供」する(オプトイン) NEXT・設計段階
| アクター/モーメント | 公開提供者・発見システム / 価値を実感した後 |
| 依存〔前提/実験〕 | C2(供給)・#3公開意向 / E2・E4 |
| 事前条件 | 既定はすべて非公開(G-5) |
| トリガー | 設定で「発見に役立てる(匿名で提供)」をオン |
基本フロー
- 何が共有されるかを明示(匿名の集計のみ・個人を特定しない)
- 同意 → 以降(+必要なら既存)の記録が visibility=公開で集計対象に
- 確認トースト「あなたの記録が匿名でみんなの発見に役立ちます」
代替・例外
- 代替 1a:「今後だけ/既存も含める」の範囲選択
- 例外 1b:同意しない → 全非公開のまま何も変わらない(G-5)
事後条件:共有プールへデータ供給(k-匿名性集計の母数に寄与)。
UC3-2 / UC3-3:公開の切替・撤回 NEXT・設計段階
| ID | トリガー | 基本フロー | 事後条件 |
|---|---|---|---|
UC3-2 切替 | 記録詳細の visibility トグル | 「この記録を発見に使う/使わない」を切替 → 即時反映、集計に追加/除外 | 当該記録の visibility 更新(機微な記録は非公開のまま) |
UC3-3 撤回 | 提供をオフ/全公開を撤回 | 提供オフ → 即時に集計対象から除外(以後の共起/近傍に出ない) | 発見プールから除外。提示済み集約は個人を含まず遡及不要 |
横断ガード(G-1〜G-5) NEXT・設計段階
複数UCが共有する例外・ポリシー。各UCはここを参照する。
| ID | ガード | 挙動 | 紐づく前提/原則 |
|---|---|---|---|
G-1 | データ不足フォールバック | 共起/近傍が出ない時は枠を出さず、アルゴ・レコメンド/回遊に静かにフォールバック(記録直後は何も出さない=OMTM保護) | C2 / E1 |
G-2 | k-匿名性 | n人以上(例 n≥5)が記録した豆/ペア/近傍のみ提示。少人数での個人特定を防ぐ | プライバシー |
G-3 | 一期一会フォールバック | 購入可能性で再ランクし、買えない豆は順位を下げる/「ショップ・産地・焙煎度プロファイル」共起に切替 | #1購入 / E1(購入可能率) |
G-4 | SNS化回避ガード | フォロー/フィード/コメント/個人プロフィール公開を持たない。出所は「みんなの記録から」等の匿名集約表記のみ | §5 トレードオフ |
G-5 | プライバシー | 既定は非公開、共有は匿名集計のみ、撤回は即時反映 | E4 / 公開意向 E2 |
出典:ディスカバリープラン - 社会的発見 を起点に、ブレインストーミング(2026-06-21)で新規設計
リスクと未解決の問い
主要リスク
未解決の問い(Open Questions)
- お気に入り店の robots.txt 許可割合は過半数に達するか(H5スパイク未実施)。
- Flow B の Vision モデルは Workers AI で精度が足りるか、hosted vision が必要か(小スパイクで確定)。
- スペシャリティ領域で送客アフィリエイトは経済的に成立するか。
- 社会的発見の記録密度(C2)は閾値に達するか/ユーザーは記録を公開してよいと思うか。
前提(要バリデーション)
- H1:愛好家は買った豆を記録したいが既存手段が面倒で続かない。
- H2:複数ショップのアプリ内回遊に需要がある。
- H3:履歴ベースのレコメンドで「出会い」が増える。
- H4:記録の摩擦は袋撮影で十分下げられる。
- H5:商品ページから実用精度(充足率80%)で情報抽出できる。
出典:プロダクト戦略(リスク・仮説)/ ディスカバリープラン / 技術選定 §13(統合)
付録:出典トレーサビリティ
本レポートは 01_Work/Projects/coffee-app/ の9ノートを再構成・要約して生成した。正式な規範要件は各原本を参照のこと。
| 本レポートのセクション | 元ノート | 扱い |
|---|---|---|
| エグゼクティブサマリー | ハブ / 戦略 / ロードマップ | 要約 |
| 主要コンセプト | 戦略 / 技術選定 / ディスカバリー | 要約 |
| 戦略サマリー | プロダクト戦略 | 保持+要約 |
| ロードマップ | アウトカムロードマップ | 保持 |
| OKR(MVP期) | OKR(MVP期) | 保持 |
| 主要ワークフロー | PRD §7.1 / ユーザーストーリー | 保持 |
| 機能要件 | ユーザーストーリー / PRD §7.2 | 保持 |
| 非機能要件・ガードレール | 技術選定 / OKR / H5スパイク | 統合・要約 |
| システム / アーキテクチャ | 技術選定 / PRD §7.3 | 保持+要約 |
| 社会的発見ディスカバリー | ディスカバリープラン - 社会的発見 | 要約 |
| ユースケース(社会的発見) | ディスカバリープラン + ブレインストーミング(2026-06-21) | 新規・設計段階 |
| リスクと未解決の問い | 戦略 / ディスカバリー / 技術選定 §13 / H5スパイク | 統合 |
デザインシステム
豆記録MVP(US-1〜US-14)のUI実装で参照するトークン体系。トーンは「明るく・ポップ」×「コーヒーの温かみ」。Expo / React Native(src/constants/theme.ts)でそのまま消費できる命名・構造で定義する。
- 明るく・ポップ+温かみ:主役はオレンジ。緑で甘くなりすぎを抑え、抹茶ラテ的なカフェ感を出す。
- 暖色ニュートラル:純グレー/純黒ではなく、わずかに茶を含んだ温かいグレー・エスプレッソ色を土台にする。
- ライト/ダーク両対応(
userInterfaceStyle: automatic)。全トークンに両モードの値を持つ。 - アクセシビリティ:テキスト/背景の主要組合せは WCAG AA(4.5:1) 以上。
- 4・8pxグリッド:余白・角丸・サイズを一定リズムで刻む。
1. カラー — ブランド
パレットは4色。各色に役割を固定し、増やさず使い切る。
primary スケール(state・背景tint用)
#FFF4E5
#FFE6C4
#FF9D23
#E58917
#C26F00
secondary スケール
#EEF3E6
#D8E4C4
#5B7E3C
#4C6B30
#3C5526
- primary
#FF9D23上の濃色文字#2B2018… 7.64:1 ✅(白文字は 2.08 で不可 → ボタン文字は濃色) - secondary
#5B7E3C上の白文字 … 4.67:1 ✅ - highlight
#FFD65A上の濃色文字 … 11.36:1 ✅(文字色には使わず装飾専用) - accent
#EA5252は単体では文字に不可(白3.60/濃4.41)→ ボタン塗りは#C53030(白文字 5.47 ✅)、赤はエラー意味に限定
2. カラー — ニュートラル(土台)
面積の大半を占める背景・サーフェス・文字・境界線。温かい茶系で統一する。
Light
#FFFFFF#FAF6F1#F2EBE2#E7DED2#2B201815.9:1#6F62565.9:1#B8AC9EDark
#17120E#211A14#2B231C#3A3128#F6F0E816.4:1#B8AC9E8.4:1#6F62563. カラー — セマンティック(状態色)
4色パレットを流用しつつ、出る文脈(トースト・インライン状態)で意味づけする。リンク/infoのみ暖色の外に置く(CTAとの誤認防止)。
| トークン | fill | text(淡背景上) | subtle 背景 | 用途 |
|---|---|---|---|---|
| success | #5B7E3C | #3C55267.4:1 | #EEF3E6 | 記録完了・同期成功 |
| warning | #FFD65A | #8A5A005.5:1 | #FFF6DC | 抽出フォールバック等の注意 |
| error | #EA5252 / 塗り#C53030 | #C530304.8:1 | #FDECEC | 失敗・削除・危険 |
| info / link | #2563EB | #2563EB5.2:1 | #EAF1FE | リンク・情報(暗所は#6BA8FF) |
4. タイポグラフィ
ブランド書体は2書体(assets/fonts/ に同梱・SIL OFL。ネイティブは expo-font config plugin(app.json)でビルド時に埋め込み、Expo Go / web は useFonts で読み込み)。
見出しは Zen Maru Gothic(丸ゴシック・温かみ)、本文・UI・数値・店名は Zen Kaku Gothic New(角ゴシック・クリーン)。
React Native はカスタムフォントの weight を合成しないため、weight ごとに別ファミリ名で登録する(theme.ts の FontFamily)。
コンポーネントには生のフォント名を書かず、ThemedText の type または FontFamily トークンを参照する。
| 役割 | 書体 | family(useFonts キー / Regular・Medium・Bold) | 用途 |
|---|---|---|---|
| heading(見出し) | Zen Maru Gothic | ZenMaruGothic-Regular/-Medium/-Bold | 画面タイトル・セクション見出し |
| body(本文・UI) | Zen Kaku Gothic New | ZenKakuGothicNew-Regular/-Medium/-Bold | 本文・UI・数値・店名・ボタン・リンク |
| mono | システム等幅 | ui-monospace ほか | コード・ID |
タイプスケール(ThemedText の type と一致。weight は対応する weight 別ファミリで表現)
| type | size / line-height | family(書体・weight) | 用途 |
|---|---|---|---|
title | 48 / 52 | Zen Maru Gothic Bold | 画面タイトル |
subtitle | 32 / 44 | Zen Maru Gothic Medium | セクション見出し |
default | 16 / 24 | Zen Kaku Gothic New Medium | 本文 |
small / smallBold | 14 / 20 | Zen Kaku Gothic New Medium / Bold | 補助・ラベル |
link / linkPrimary | 14 / 30 | Zen Kaku Gothic New Regular | リンク(color = info) |
code | 12 | システム等幅(Fonts.mono) | コード・ID |
5. スペーシング・レイアウト
スペーシング(theme.ts の Spacing・4px基準):half 2 / one 4 / two 8 / three 16 / four 24 / five 32 / six 64
角丸(radius):xs 6 / sm 10 / md 14(ボタン・入力)/ lg 18(カード・シート)/ pill 999(チップ・バッジ)
エレベーション(影):sm 0 1 3 rgba(0,0,0,.06) / md 0 4 16 rgba(0,0,0,.08)(iOS は shadow、Android は elevation 1/4 に対応)
レイアウト定数:MaxContentWidth 800 / BottomTabInset iOS 50 · Android 80
6. コンポーネント
ボタン — primary(濃色文字)/secondary(白文字)/outline/ghost/danger/disabled。半径 md 14、パディング 12×20(sm は 8×14)。
state:通常 500 → press 600 → active 700。
入力フォーム — ラベル+入力+ヘルパー。focus はオレンジ枠+tintリング、error は赤枠+赤ヘルパー。
enum picker — 少数択はセグメント、多数択は選択チップ(焙煎度・精製など)。
カード/バッジ/リスト行/空状態
フローラルで明るい酸。浅煎り・ウォッシュト。
トースト — 状態色の subtle 背景+text色。
ダークモード(同コンポーネント・暗背景)
7. トークン化(Expo / React Native)
既存の flat な Colors.light / Colors.dark 構造を維持してキーを追加する。ThemedText / ThemedView の themeColor / type でそのまま参照でき、既存コンポーネントは無改修で新配色に切り替わる。themed-text.tsx のハードコード #3c87f7 は info トークン参照に置換する。
// src/constants/theme.ts
export const Colors = {
light: {
background: '#FFFFFF', surface: '#FAF6F1', surfaceAlt: '#F2EBE2', border: '#E7DED2',
text: '#2B2018', textSecondary: '#6F6256', textMuted: '#B8AC9E',
// brand
primary: '#FF9D23', primaryHover: '#E58917', primaryActive: '#C26F00',
primarySubtle: '#FFF4E5', onPrimary: '#2B2018',
secondary: '#5B7E3C', secondaryHover: '#4C6B30', secondaryActive: '#3C5526',
secondarySubtle: '#EEF3E6', onSecondary: '#FFFFFF',
highlight: '#FFD65A', onHighlight: '#2B2018',
// semantic
success: '#5B7E3C', successText: '#3C5526', successSubtle: '#EEF3E6',
warning: '#FFD65A', warningText: '#8A5A00', warningSubtle: '#FFF6DC',
error: '#EA5252', errorStrong: '#C53030', errorText: '#C53030', errorSubtle: '#FDECEC',
info: '#2563EB', infoSubtle: '#EAF1FE',
},
dark: {
background: '#17120E', surface: '#211A14', surfaceAlt: '#2B231C', border: '#3A3128',
text: '#F6F0E8', textSecondary: '#B8AC9E', textMuted: '#6F6256',
primary: '#FF9D23', primaryHover: '#FFB04D', primaryActive: '#E58917',
primarySubtle: '#2E2114', onPrimary: '#2B2018',
secondary: '#7AA255', secondaryHover: '#8BB266', secondaryActive: '#5B7E3C',
secondarySubtle: '#232A1A', onSecondary: '#10160A',
highlight: '#FFD65A', onHighlight: '#2B2018',
success: '#7AA255', successText: '#C3DCA3', successSubtle: '#1E2618',
warning: '#FFD65A', warningText: '#F2C66B', warningSubtle: '#2E2613',
error: '#FF6B6B', errorStrong: '#C53030', errorText: '#FF8A8A', errorSubtle: '#2E1818',
info: '#6BA8FF', infoSubtle: '#16233A',
},
} as const;
export const Radius = { xs: 6, sm: 10, md: 14, lg: 18, pill: 999 } as const;
// Spacing / Fonts は既存の theme.ts を踏襲(half/one/two/three/four/five/six)
設計:本レポート「カラー」ブレインストーミング(2026-06-25)/ 既存 src/constants/theme.ts・themed-text.tsx / Issue #16。コントラスト比は sRGB相対輝度で実測(WCAG 2.1)。