☕ スペシャリティコーヒー発見アプリ

プロダクト仕様 統合リーダブルレポート — 戦略から検証まで一望する

種別: 製品仕様サマリー 版: MVP期 / 2026-06 生成日: 2026-06-21 対象読者: プロダクト+エンジニアリング 出典: coffee-app/ 9ノート

エグゼクティブサマリー

複数の焙煎所を買い回る愛好家のための「コーヒー履歴書」を育て、まだ知らない最高の豆との出会いを毎週の習慣にするプロダクト。自社で豆を売らず(在庫を持たず)、最高の豆があるショップへ中立に送客する「発見プラットフォーム」を志向する。

北極星指標は月間「出会い数」(新規に記録された豆の種類数)。ただしMVP(NOWフェーズ)の最優先は、その入力指標である記録の習慣化を証明すること。戦略上の最大リスクは「記録の手間>価値になり習慣化しない」ことであり、本フェーズの機能・OKR・技術選定はすべて記録の摩擦を極限まで下げる一点に収束している。

記録経路は2つを補完的に用意する。オンライン購入は「見ている商品ページからサーバーサイドAI抽出(Cloudflare /crawl)でワンタップ取り込み」、店頭・他所購入は「袋を撮影してAI抽出」。抽出が失敗しても記録は残す楽観更新を貫き、摩擦で記録を失わせない。回遊・レコメンド・社会的発見といった「発見エンジン」はNEXT以降に位置づけ、NOWでは混ぜない。

ロードマップ段階
3
NOW(記録習慣化)→ NEXT(発見)→ LATER(送客で収益化)。現在地は NOW
OKR Key Results
3
採用=案A:Activation 60% / 週次継続 40%(OMTM) / 月4袋
ユーザーストーリー
14
US-1〜14(P0:5 / P1:7 / P2:2)→ 62サブタスク
検証すべき仮説
5 + 3
記録系 H1〜H5/社会的発見 C1〜C3
戦略リスク(Top)
4
習慣化・豆マスタ・送客マネタイズ・抽出依存
月額ランニング
≒ $5
実費は Cloudflare のみ。他は無料枠(Supabase/PostHog/EAS/GH Actions)

主要コンセプト

本プロダクトを読み解くための中核用語。以降の各セクションはこれらを前提に記述する。

North Star=月間出会い数
事業成功の先行指標。新規に記録された豆の種類数。回遊・記録・再訪のすべてが効く合成指標。
OMTM=記録継続率
今四半期に最適化する唯一の指標。「買ったら記録する」習慣の定着(週次 W1→W4)。
マルチショップ・ジプシー
複数の焙煎所/ショップを横断的に買い回る愛好家。一次ターゲットセグメント。
JTBD
「次の一杯でまだ知らない最高の豆に出会いたい。でも何を買ったか覚えきれず再訪できない」。
コーヒー履歴書
ユーザーごとに蓄積する嗜好履歴。スイッチングコスト=データモートの源泉。
送客モデル(中立)
自社EC・在庫・決済を持たず、最高の豆があるショップへ送客。中立性が信頼の差別化。
要石スキーマ(1スキーマ3役)
豆JSON Schema=①抽出の契約 ②記録DBの構造 ③将来レコメンドの入力 を兼ねる中心資産。
楽観更新
「記録を先に作る」→抽出は後追いで充填。抽出失敗でも記録は残し、摩擦で記録を失わせない設計原則。
一期一会問題
スペシャリティは同じ豆が再入荷しにくい。出会わせても買えなければ送客もマネタイズも立たないという制約。
社会的発見(匿名共起)
他者の記録を発見に使う第3エンジン候補。フォロー/フィードを持たず匿名集約で SNS 化を回避。

戦略サマリー

ビジョン

最高のスペシャリティコーヒーとの出会いを、毎週の習慣にする。掲げる価値は発見>効率/送客の中立性(自社で売らない)/記録は資産。

ターゲットと立ち位置

一次セグメントはマルチショップ・ジプシー(課題の熱量が最も高く、購入種類が多くデータが濃く、口コミ伝播力がある/PM自身がユーザーでドッグフーディング可能)。相対コストはプレミアム価値型で、安さではなく「あなたにとっての最高の一杯への最短距離」を売る。明示的に最初は狙わない層=大手チェーン中心のライトユーザー、業務用バイヤー。

戦略的トレードオフ(やらないこと)

戦略の本質は「何をやらないか」。下表が価値を増幅する意思決定である。

やらない代わりになぜ
自社EC・決済を持つショップへの送客に徹する在庫リスク・オペコストを負わず、中立な発見プラットフォームの信頼を守る
抽出レシピ/抽出ログの深掘り「豆との出会い」に集中既存の抽出系アプリと正面衝突せず、コア体験を薄めない
大型SNS/コミュニティ化個人の記録体験を磨く初期はネットワーク前提機能に依存せず、単独でも価値が立つ設計(※社会的発見は匿名共起で両立を検証)
大手チェーンの網羅スペシャリティ特化ライト層は課題が浅く差別化が効かない
多機能化(バリスタ向け計測等)記録→発見のループに一点集中MVPでの習慣化検証を最優先、機能の散漫化を防ぐ

成長エンジンと防御性

基本は Product-Led Growth。記録が貯まるほどレコメンド精度が上がり送客が増え、提携が広がって記録対象も広がるループを回す。

flowchart LR A[豆を記録] --> B[履歴が貯まる] B --> C[レコメンド精度が上がる] C --> D[ショップへ送客] D --> E[ショップ提携が増える] E --> F[品揃え・記録対象が広がる] F --> A style A fill:#e7ecff,color:#000 style C fill:#635bff,color:#fff style E fill:#4b45c6,color:#fff
図1. 成長エンジン(PLGループ)— 記録データが後段すべての価値を規定する

防御性:データモート(嗜好履歴のスイッチングコスト)/ネットワーク効果(記録が豆マスタとレコメンド精度を共同で高める)/中立ブランド(自社ECを持つ競合には取りづらい)/送客実績に基づくショップ関係。MVP段階ではモートは薄く、守り方は「ニッチ深耕 × ショップ関係 × データ密度」の三重化。

出典:プロダクト戦略ノート(要約・一部保持)

ロードマップ

機能ではなくアウトカム(誰が・何をできるように・なぜ)で記述。各フェーズは前フェーズの検証済み仮説を土台に開く。前が証明できなければ次へ進まない。

flowchart LR N["NOW / 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
図2. Now-Next-Later 全体像
フェーズアウトカム主な取り組み
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-8
Should US-2 US-10 US-12 US-13 US-15 US-16
Could US-3
KR1 Activation / KR2 継続
L3 入力経路 お気に入り店を見ながら/袋を撮るだけで記録を起こせる。オンライン・店頭の双方で記録の摩擦を下げる。 Should US-4 US-7
Could 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期)

採用:案A(2026-06-12 決定)

3案(習慣化 / 課題の確信 / 摩擦最小化)から案A(習慣化)を主OKRに採用。案Cのガードレールを併走、案BのKR1(インタビュー)を着手前チェックとして先行実施する。

Objective:愛好家が「買ったら記録する」を、無意識の習慣にする。

IDKey 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:オンライン購入 → ページ抽出(主経路)

sequenceDiagram participant U as ユーザー participant App as アプリ(WebView) participant W as Worker/Edge Fn participant CF as Cloudflare /crawl U->>App: お気に入り店を閲覧し購入 U->>App: 「記録」タップ App->>App: 記録を即時作成(楽観更新・"解析中"表示) App->>W: 商品URLを送信 W->>CF: 豆JSON Schema + AI抽出指示で /crawl CF-->>W: 構造化JSON(ポーリングで取得) W-->>App: 抽出結果 App->>U: フォーム自動充填 → 確認/微修正 → 保存
図3. フローA — ページ抽出による記録(主経路)
  1. 記録の即時作成 — 「記録」タップで source_url 付きの記録を先に作る(楽観更新)。
  2. サーバーサイド抽出 — Worker/Edge Function が /crawl を実行。トークンはサーバ内に秘匿。
  3. 自動充填と確認 — スキーマ各項目を充填、記載のない項目は null(推測で埋めない)。ユーザーが確認・微修正して保存。
  4. ガードレール — 取り込み〜充填は 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豆を手動で記録する(土台)MustConfirmed必須=name/shop/purchased_at、任意は空可、enum選択、最小入力 ≤30秒
US-8初回起動で好みを登録し、おすすめから初日に1袋記録MustConfirmed好み登録(スキップ可)→おすすめ→推薦経由で初回記録/送客、達成FB / Activation(推薦経由/直接を区別)
US-9記録イベントを計測する(PM視点)MustConfirmedActivation/継続/所要時間/抽出品質を非同期計測 / 全KR
US-5商品ページから取り込み自動充填(主経路)MustConfirmed楽観更新→解析中→自動充填→確認保存、取り込み ≤10秒 / 充足率80%
US-2記録した豆を一覧・詳細で見返すShouldConfirmed新しい順一覧、詳細、source_urlから再注文、空状態 / 継続
US-4WebViewでお気に入り店の商品ページを開くShouldConfirmed数店限定、現在URL取得して記録へ渡す / カバレッジ
US-7袋を撮影して情報を自動充填するShouldConfirmed撮影→抽出充填→確認保存、image_url保存、撮影〜保存 ≤60秒
US-6抽出不可時に撮影/手動へフォールバックShouldConfirmedrobots拒否は撮影案内、作りかけの記録を失わない
US-3記録した豆を編集・削除するCouldConfirmed全項目編集、削除確認、自動充填値の上書き / 信頼性
US-11アカウント作成・ログイン・ログアウト 網羅補完MustConfirmed外部認証(Supabase)ラッパー、セッション永続化、user_id+RLS紐付け / 複数端末の土台
US-10お気に入り店(対象店)を登録・編集・削除 網羅補完ShouldConfirmed店名+URL登録、WebView許可リスト連携、抽出不可フラグ / US-4の前提(回遊管理はNEXT対象外)
US-13カメラ権限の要求と拒否時フォールバック 網羅補完ShouldConfirmed権限要求→拒否時は写真選択/手動へ、再許可導線、クラッシュしない / US-7の前提
US-12公開設定(visibility)の布石・既定private 網羅補完ShouldConfirmedvisibilityカラム追加、既定private、§9集計の除外保証 / 社会的発見NEXTの前提
US-14オフライン記録→復帰後の抽出・同期 網羅補完Couldスパイク要オフライン一次保存、復帰時に冪等同期、source_url記録は復帰後 /crawl 充填 / 信頼性
US-15好みを登録・編集する(コールドスタート好みの入力) §3.5 NOWShouldConfirmed申告好み(焙煎度/フレーバー/産地)の登録・編集・リセット、スキップ可、挽き方除外 / US-8初回・US-16推薦の入力
US-16ホーム「あなたにおすすめの豆」で好みに合う豆と出会う §3.5 NOW(手動シード)Should設計段階好み×今買えるカタログを照合→推薦カード→記録/送客、空状態フォールバック / 転換率・一致率(E7)
flowchart TB subgraph L0["基盤層"] US11["US-11 認証(外部委譲)"] end subgraph L1["土台層"] US1["US-1 手動記録"] end subgraph L2["記録の周辺・前提(並列可)"] US2["US-2 一覧/詳細"] US3["US-3 編集/削除"] US8["US-8 初回:好み→おすすめ→記録"] US9["US-9 計測"] US12["US-12 visibility布石"] US10["US-10 対象店の登録"] US13["US-13 カメラ権限"] US15["US-15 好み(データ/設定)"] US16["US-16 おすすめ面(NOW)"] end subgraph L3["入力経路"] US4["US-4 WebView閲覧"] US7["US-7 撮影充填"] US14["US-14 オフライン同期"] end subgraph L4["主経路抽出"] US5["US-5 ページ抽出充填"] end subgraph L5["回復"] US6["US-6 フォールバック"] end US11 --> US1 US11 --> US15 US15 --> US16 & US8 US16 --> US8 US1 --> US2 & US3 & US8 & US9 & US12 & US14 US10 --> US4 US1 --> US4 US13 --> US7 US1 --> US7 US4 --> US5 US5 --> US6 US7 -.失敗時.-> US6 US1 -.最終手段.-> US6 style US11 fill:#635bff,color:#fff style US1 fill:#e7ecff,color:#000 style US5 fill:#635bff,color:#fff style US10 fill:#fff0d9,color:#000 style US13 fill:#fff0d9,color:#000 style US15 fill:#d8e4c4,color:#000 style US16 fill:#d8e4c4,color:#000
図4. ユーザーストーリーの通し依存マップ(全16ストーリー/US-15・US-16 は §3.5 コールドスタート好み・NOW手動シード)

実装サブタスク分解(リファインメント: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.1Expo×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

依存:US-11 | ← 機能要件(一覧)に戻る

Description

愛好家として、買った豆の情報(産地・焙煎度・購入元など)を手で記録したい。袋を見ながら確実に残せるように。

受け入れ条件

  1. 記録フォームに豆スキーマ(PRD 7.3)の各項目が入力できる。必須は name / shop / purchased_at(既定=当日)。
  2. 任意項目(産地・品種・精製・焙煎度・焙煎日・内容量・価格・テイスティングノート・メモ・画像)は空のまま保存できる。
  3. 保存すると記録が永続化され、再起動後も残る。
  4. purchased_at は既定で当日が入り、ワンタップで保存まで到達できる。
  5. process / roast_level は選択肢(enum)から選べる。
  6. (ガードレール)最小入力(必須のみ)で30秒以内に保存完了できる。

実装サブタスク

ID内容サイズ依存元AC
US-1.1豆スキーマのデータモデル定義+Supabase テーブル/マイグレーション(PRD7.3全項目・RLSでユーザー別保護)S(~1d)US-111,3
US-1.2記録フォームUI(必須3項目+任意、process/roast_level は enum picker、purchased_at既定=当日)M(~2d)US-1.11,2,5
US-1.3保存・永続化ロジック(Supabase insert+ローカル反映、再起動後保持)S(~1d)US-1.13
US-1.4最小入力=30秒フロー+所要時間計測フック(US-9連携・PostHog)S(~1d)US-1.24,6

US-2:記録した豆を一覧・詳細で見返す P1 / Should

依存:US-1 | ← 機能要件(一覧)に戻る

NOTE:好みの傾向可視化(コーヒー履歴書)はNEXT。ここは最小の一覧+詳細に限定。

Description

愛好家として、記録した豆を一覧で見返し、1件をタップして詳細を見たい。「残っている」価値を実感できるように。

受け入れ条件

  1. 記録した豆が新しい順に一覧表示される(銘柄・店・購入日・サムネ)。
  2. 1件タップで全項目の詳細が見られる。
  3. source_url があれば、詳細から元の商品ページを開ける(再注文導線)。
  4. 1件も記録がないときは、最初の記録を促す空状態が表示される。
  5. 画像があればサムネ表示、なければプレースホルダ。

実装サブタスク

ID内容サイズ依存元AC
US-2.1新しい順一覧(銘柄/店/購入日/サムネ、TanStack Query、空状態、画像/プレースホルダ)M(~2d)US-11,4,5
US-2.2詳細画面(全項目、Expo Router typed route、null項目の表示統一、解析中表示配慮)M(~2d)US-2.12
US-2.3source_url 商品ページを開く導線(Linking・URLバリデート・再注文計測)S(~1d)US-2.23

❓ Open Q:空状態(AC4)の誘導先はUS-8と重複しうる。一覧空状態は単純導線に限定し初回フローはUS-8へ寄せるか。

US-3:記録した豆を編集・削除する P2 / Could

依存:US-1 | ← 機能要件(一覧)に戻る

Description

愛好家として、記録した豆を後から編集・削除したい。自動充填の間違いや入力ミスを直せるように。

受け入れ条件

  1. 詳細から編集に入り、全項目を修正して保存できる。
  2. 削除でき、削除前に確認が出る。
  3. 編集内容は即時反映され、永続化される。
  4. 自動充填で入った値も手で上書きできる。

実装サブタスク

ID内容サイズ依存元AC
US-3.1編集(US-1フォーム再利用・プリフィル・update mutation・即時反映・自動充填値上書き)M(~2d)US-1, US-2.21,3,4
US-3.2削除(確認ダイアログ・delete・楽観更新+ロールバック・画像実体削除方針)S(~1d)US-2.22

❓ Open Q:削除時にSupabase Storage上の袋写真も物理削除するか(孤児ファイル許容か)。

US-4:WebViewでお気に入り店の商品ページを開く P1 / Should

依存:US-10, US-1 | ← 機能要件(一覧)に戻る

NOTE:回遊・お気に入り管理という発見体験はNEXT。ここは記録のための最小WebView(指定URL/数店を開けるだけ)。

Description

愛好家として、アプリ内のWebViewでお気に入り店の商品ページを閲覧したい。タブを開かずそのまま記録に進めるように。

受け入れ条件

  1. 登録した数店の商品ページをアプリ内WebViewで表示できる。
  2. 一般的なページ操作(スクロール・リンク遷移・戻る)ができる。
  3. 表示中ページのURLが取得でき、「記録」アクションに渡せる。
  4. 対応店は数店に限定(NEXTで拡大)。

実装サブタスク

ID内容サイズ依存元AC
US-4.1react-native-webview組込+対象店ホワイトリスト表示(対象外URLは拒否)M(~2d)US-101,4
US-4.2一般操作(スクロール/遷移/戻る/再読込/外部ブラウザ、履歴管理)S(~1d)US-4.12
US-4.3表示中URL取得→「記録」へ source_url 受け渡しS(~1d)US-4.13

❓ Open Q:ホワイトリストの管理場所(Supabase shops か同梱定数)。SPA系ECでのURL取得漏れ検証。

US-5:見ている商品ページから情報を取り込んで自動充填する(主経路) P0 / Must

依存:US-4 | ← 機能要件(一覧)に戻る

Description

愛好家として、WebViewで見ている商品ページからワンタップで豆情報を取り込み、記録フォームに自動充填したい。入力の手間をほぼゼロにできるように。

受け入れ条件

  1. 「記録」タップでまず記録が即時作成され、source_url が保存される(楽観更新)。
  2. 抽出中は「解析中」が表示され、ユーザーは待たずに他操作できる。
  3. 抽出完了で、スキーマ各項目がフォームに自動充填される(Cloudflare /crawl、サーバーサイド)。
  4. 充填後、ユーザーが確認・微修正して保存できる。
  5. 記載のない項目は空(null)で、推測値は入れない。
  6. (ガードレール)取り込み〜充填が10秒以内に完了する。
  7. 抽出失敗・タイムアウト時は記録自体は残り、US-6のフォールバックへ誘導する。

実装サブタスク

ID内容サイズ依存元AC
US-5.0【スパイク】代表3〜5店で /crawl充足率80%+充填10秒を実測、prompt/Schema初版確定M(~2d)US-43,5,6
US-5.1Worker /crawlラッパー(Schema+prompt契約固定・jobId返却・null固定/推測禁止)M(~2d)US-5.03,5
US-5.2非同期ポーリング/結果取得(processing/done/failed・10秒境界・failed正規化)M(~2d)US-5.16,7
US-5.3楽観更新(「記録」タップで即時作成+source_url保存、裏でジョブ起動)S(~1d)US-4, US-5.11
US-5.4「解析中」非ブロッキングUI(待ち中も編集可・ジョブ状態購読)M(~2d)US-5.2,5.32
US-5.5完了→フォーム自動充填(null項目は空、確認・微修正して保存)M(~2d)US-5.43,4,5
US-5.6失敗/タイムアウト処理(記録保持・US-6導線へ誘導・イベント発火)S(~1d)US-5.2,5.37
US-5.7計測(レイテンシ/充足率/成功失敗率、10秒・80%ガードレール監視)S(~1d)US-5.4,5.56

❓ Open Q:対象店の代表集合は確定済みか。80%未達時の最低成立項目(name/shop/price等)。ポーリング間隔とジョブ保持TTL。

US-6:抽出が使えないときに撮影/手動へフォールバックする P1 / Should

依存:US-5(US-1/US-7へ退避) | ← 機能要件(一覧)に戻る

Description

愛好家として、ページ抽出が失敗・拒否・低精度のとき、撮影か手動入力にスムーズに切り替えたい。どんな購入でも記録を諦めずに済むように。

受け入れ条件

  1. robots.txt拒否などで抽出不可な店では、抽出を試みず撮影/手動を案内する。
  2. 抽出が失敗/タイムアウトしたら、撮影 or 手動入力の導線が出る。
  3. どの経路でも作りかけの記録は失われない(再入力不要)。
  4. 抽出結果が明らかに不十分でも、ユーザーが手で補完して保存できる。

実装サブタスク

ID内容サイズ依存元AC
US-6.1事前判定(robots不可な店は抽出せず撮影/手動案内、Worker判定→通知)M(~2d)US-5.1,5.21
US-6.2失敗後フォールバック導線(US-5.6 failed受領で撮影/手動の選択UI)S(~1d)US-5.62
US-6.3記録の引き継ぎ(作りかけ記録・既充填分を保持し再入力不要で遷移)M(~2d)US-5.3, US-6.23
US-6.4手動補完保存(部分充填+手入力の混在で保存完了)S(~1d)US-6.34

❓ Open Q:「低精度」の判定基準(充足率しきい値 or 必須項目欠落)。撮影経路のOCR有無はMVP範囲か。

US-7:袋を撮影して情報を自動充填する P1 / Should

依存:US-13, US-1 | ← 機能要件(一覧)に戻る

Description

愛好家として、店頭や他所で買った豆の袋を撮影して情報を自動充填したい。オンライン以外の購入も手早く記録できるように。

受け入れ条件

  1. 「記録」→ 撮影で袋の写真を撮れる(または既存写真を選べる)。
  2. 画像から産地・焙煎度等を抽出してフォームに自動充填する。
  3. 撮影画像は記録の image_url として保存される。
  4. 充填後、確認・微修正して保存できる。記載なしは空、推測埋めしない。
  5. (ガードレール)撮影〜保存が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-131
US-7.2画像をSupabase Storageへ上げ image_url 保存(先に記録作成→後追い充填)M(~2d)US-7.13
US-7.3US-7.0方式で画像→Schema項目抽出→自動充填(記載なしは空・推測埋めしない)L(~3d)US-7.0, US-7.12,4
US-7.4充填後の確認・微修正・保存+撮影〜保存60秒計測S(~1d)US-7.34,5

❓ Open Q:抽出はクライアント/Worker どちらか。日本語OCR精度(roast_date等)。抽出失敗時の全項目空フォールバック。

US-8:初回起動で好みを登録し、おすすめから初日に1袋記録する P0 / Must

依存:US-1, US-9, US-15, US-16 | ← 機能要件(一覧)に戻る

NOTE:本アプリはオンボーディングが要るほど複雑ではない。重いガイド型オンボ(3経路ウェルカム)は廃止し、初回起動=軽い好み登録 →「あなたにおすすめの豆」→ 推薦から記録/送客に作り替える。Activation(KR1)は初回記録(推薦経由)を維持。3経路(抽出/撮影/手動)は常時使える記録手段として残す。

Description

新規ユーザーとして、初回起動で自分の好み(焙煎度・フレーバー・産地)をすばやく登録し、最初から「好みに合う・今買える豆」のおすすめを見て、そこから初日のうちに最初の1袋を記録 or 送客で出会えるようにしたい。

受け入れ条件

  1. 初回起動で、好みを 2-3タップ・スキップ可 で登録できる(入力は US-15 と同一・§3.5 #3)。
  2. 好み登録直後(スキップ時も)、ホーム「あなたにおすすめの豆」が表示される。未登録/カタログ薄でも人気・季節でフォールバックし空にしない(G-3)。
  3. おすすめカードから 記録(推薦経由・catalog_bean_id 紐付け)または送客 ができる(US-16)。
  4. 好みを使わず、3経路(ページ抽出/撮影/手動)で直接記録することもできる。
  5. 最初の1袋を保存すると、達成感のあるフィードバックが出る。
  6. (KR1)登録〜初日の初回記録到達を計測でき、推薦経由/直接を区別する(US-9連携)。

実装サブタスク

ID内容サイズ依存元AC
US-8.1初回起動=好み登録(2-3タップ・スキップ可)→ ホーム「あなたにおすすめの豆」着地(app_first_open発火・US-15 入力を再利用)M(~2d)US-15, US-16, US-9.11,2
US-8.2おすすめ経由の初回記録(推薦カード→記録/送客・catalog_bean_id アトリビューション)+達成フィードバックM(~2d)US-8.1, US-163,5
US-8.3計測:登録〜初日初回記録到達を推薦経由/直接で区別(US-9.1 ファネル拡張:preference_set→recommend_view→bean_recorded)S(~1d)US-8.1, US-9.16

旧US-8(3経路ガイド型オンボで初回記録)から、好み先行・推薦経由の初回記録へ作り替え(2026-06-26)。重いオンボは廃止、3経路は常設の記録手段として存続。❓ Open Q:好みスキップ時の初回ホームの空状態コピー/推薦経由 vs 直接の達成FB差。

US-9:記録イベントを計測する(PM視点・PostHog) P0 / Must

依存:US-1 | ← 機能要件(一覧)に戻る

Description

PMとして、記録のActivation・継続・所要時間・抽出品質を計測したい。OKR案Aの達成を数字で判断できるように。

受け入れ条件

  1. 「初回記録到達」イベントを、登録からの経過とともに記録する(Activation率)。
  2. ユーザー別の週次記録有無を集計できる(W1→W4継続=OMTM)。
  3. 記録1件あたりの所要時間を経路別(抽出/撮影/手動)に計測する。
  4. ページ抽出の項目自動充足率・誤り率・レイテンシを記録する。
  5. 月間記録豆数をユーザー別に集計できる。
  6. 計測は記録体験を遅くしない(非同期送信)。

実装サブタスク

ID内容サイズ依存元AC
US-9.1Activation基盤(app_first_open→preference_set(任意)→recommend_view→bean_record_started→bean_recorded、24hファネル、推薦経由/直接の区別、非同期送信共通化)M(~2d)US-11,6
US-9.2継続&ボリューム(週次リテンションW1→W4、月間記録豆数はPostgres真実源)S(~1d)US-9.12,5
US-9.3所要時間計測(経路別 duration_ms、各フローに埋め込み)M(~2d)US-9.13
US-9.4抽出品質計測(充足率/誤り率/レイテンシ、extraction_logs永続、誤り率はUS-3修正検知に依存)M(~2d)US-9.1, US-54

❓ Open Q:誤り率の「修正項目」確定窓(初回保存差分 or 後日編集も含むか)。

US-10:お気に入り店(対象店)を登録・編集・削除する P1 / Should

依存:(独立・US-4の前提) | ← 機能要件(一覧)に戻る

NOTE:回遊/キュレーションという発見体験はNEXT。ここは記録のための最小の店設定(店名+URL/ドメインの登録)に限定。v1ではPMが1〜2店をハードコードする運用でも代替可だが、ユースケースとして明示する。

Description

愛好家として、WebView閲覧とページ抽出の対象店を自分で登録・編集・削除したい。US-4で「登録した数店」を開ける前提を成立させ、買い回る店の変化に追従できるように。

受け入れ条件

  1. 店名と商品ページURL(またはドメイン)を指定して対象店を登録できる。
  2. 登録した店が一覧表示され、US-4のWebViewはこの一覧の店のみを開ける。
  3. 登録した店の名前・URLを編集でき、削除できる(削除後はその店URLが新規記録の抽出対象から外れる)。
  4. robots.txt等で抽出不可と判明した店には「撮影/手動推奨」フラグを表示する(US-6と連携)。
  5. 対象店が0件のときは、最初の店登録を促す空状態を表示する。
  6. (スコープ)対象は数店に限定。発見・回遊のためのブラウズ機能は持たない。

実装サブタスク

ID内容サイズ依存元AC
US-10.1shopsテーブル定義(name/url/capture_flag/user_id)+RLS+マイグレーションS(~1d)—1,3
US-10.2店登録フォーム(店名+URL/ドメイン正規化バリデーション→insert)M(~2d)US-10.11
US-10.3登録店一覧(取得/表示/編集・削除導線、削除後は抽出対象外)M(~2d)US-10.1,22,3
US-10.4店編集(プリフィル→update)S(~1d)US-10.33
US-10.5WebView許可リスト連携(onShouldStartLoadWithRequestで一覧ドメインのみ許可)M(~2d)US-10.32,6
US-10.6robots.txt 抽出不可判定→capture_flag+「撮影/手動推奨」フラグ(US-6連携)M(~2d)US-10.1,34
US-10.70件時の空状態UI(「最初の店を登録」CTA)S(~1d)US-10.35

❓ Open Q:URLとドメインどちらを正に(抽出キー)。capture_flagは自動のみ/手動上書き可か。

US-11:アカウント作成・ログイン・ログアウト P0 / Must

依存:外部認証(Supabase) | ← 機能要件(一覧)に戻る

NOTE:認証は自前実装しない(PRD§7.3)。Supabase Auth等の外部認証をラップするUC。

Description

愛好家として、アカウントを作ってログインし、記録を自分のものとして保持したい。機種変更や複数端末でも同じコーヒー履歴書にアクセスできるように。

受け入れ条件

  1. 外部認証(メールリンク/OAuth等、自前パスワード保持なし)でアカウント作成・ログインできる。
  2. ログイン状態が永続化され、再起動後も維持される。
  3. ログアウトでき、ログアウト後はローカルの記録が他者に見えない状態になる。
  4. 認証アカウントと記録データが紐付き、複数端末から同一記録にアクセスできる土台になる。
  5. 認証の失敗・キャンセル時に明確なエラーを示し、記録機能の入口で詰まらせない。

実装サブタスク

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.11
US-11.3セッション永続化(AsyncStorage/SecureStore・autoRefresh・起動時復元・ルートガード)M(~2d)11.22
US-11.4ログアウト(signOut+ローカルキャッシュクリア・他者非露出確認)S(~1d)11.33
US-11.5記録紐付け土台(beans.user_id+RLS本人のみCRUD・複数端末アクセス確認)M(~2d)11.34
US-11.6認証失敗/キャンセルのエラーUX(非ブロッキング・PostHog計測)S(~1d)11.25

❓ Open Q:匿名先行利用(未ログインでローカル記録→後で紐付け)を認めるか(US-8の摩擦と連鎖)。NOWの認証方式は1つに絞れるか。

US-12:公開設定(visibility)の布石・既定private P1 / Should

依存:US-1 | ← 機能要件(一覧)に戻る

NOTE:PRD§9.6が「NOWで先行追加する布石」と明記。NOWでは公開トグルUIの本格露出はせず、データとして保持・送信されることを保証するのが目的。

Description

PMとして、各豆記録にvisibility(private/public)フィールドを先行追加し、既定をprivateにしておきたい。NEXTの社会的発見(PRD§9)を、NOWのデータ構造の布石として安全に準備できるように。

受け入れ条件

  1. すべての豆記録が visibility(private/public)を持ち、既定は private。
  2. 既存記録・新規記録のいずれにも既定 private が確実に入る(マイグレーション含む)。
  3. NOWでは切り替えUIは最小または非露出でよいが、値はDBに保持されサーバーへ送信される。
  4. visibility=private の記録は、将来の社会的発見(§9)の集計対象に絶対に含まれないことが保証される。

実装サブタスク

ID内容サイズ依存元AC
US-12.1beans.visibility(enum, NOT NULL DEFAULT 'private') 追加+既存行マイグレーション、型/Schema反映、UIは非露出/最小M(~2d)US-11.51,2,3
US-12.2private除外の保証(公開用ビュー or RLSでpublic限定)+private混入しないリグレッションテストS(~1d)12.14

US-13:カメラ権限の要求と拒否時フォールバック P1 / Should

依存:(独立・US-7の前提) | ← 機能要件(一覧)に戻る

Description

愛好家として、袋撮影(US-7)の前にカメラ権限を求められ、拒否しても記録を諦めずに済むようにしたい。権限の有無で記録体験が壊れないように。

受け入れ条件

  1. 撮影記録の初回時にカメラ権限をリクエストする。
  2. 許可時は撮影に進み、拒否時は既存写真の選択 or 手動入力(US-1)へフォールバックする。
  3. 権限拒否後も、OS設定から再許可へ誘導する導線を示す。
  4. 権限状態にかかわらずアプリがクラッシュしない。
  5. (ガードレール)権限ダイアログ〜記録開始までユーザーを迷わせない。

実装サブタスク

ID内容サイズ依存元AC
US-13.1権限ステータス管理(useCameraPermissions・undetermined/granted/denied 中央管理)M(~2d)—1,4
US-13.2許可時フロー(撮影画面US-7へ遷移)S(~1d)US-13.12
US-13.3拒否時フォールバックUI(既存写真選択/手動入力US-1へ接続)M(~2d)US-13.12
US-13.4再許可導線(Linking.openSettings()+復帰時の権限再チェック)S(~1d)US-13.13
US-13.5事前説明(pre-permission priming)画面S(~1d)US-13.15
US-13.6Maestro E2E(許可/拒否/設定復帰・クラッシュしない・計測)M(~2d)US-13.2,3,44,5

❓ Open Q:iOS/Androidの再要求挙動差の反映。写真選択のライブラリ権限拒否の二次フォールバック範囲。

US-14:オフライン記録→復帰後の抽出・同期 P2 / Could

依存:US-1 | ← 機能要件(一覧)に戻る

NOTE:技術スパイク要:ローカルDB+同期戦略の検証が前提。見積前に調査が必要。

Description

愛好家として、電波のない店頭でも撮影・手動で記録を作れ、オンライン復帰時に自動で同期・抽出されるようにしたい。通信状況で記録を失わないように。

受け入れ条件

  1. オフライン時でも手動入力・袋撮影で記録を作成し、ローカルに保存できる。
  2. オンライン復帰時、未送信の記録がサーバーへ自動同期される。
  3. オフラインで作成した(URL付き)記録は、復帰後に /crawl 抽出が走りフォーム充填される。
  4. 同期で記録が重複・競合しない(記録は一意に保たれる)。
  5. 各記録の同期状態(未同期/同期済み)がユーザーに分かる。

実装サブタスク

ID内容サイズ依存元AC
US-14.0【スパイク】ローカルDB選定+同期戦略(UUID採番・冪等upsert・競合解決・NetInfo復帰検知)L(~3d)—1,2,4
US-14.1オフライン時の手動/撮影記録をローカルDBへ一次書込(UUID主キー)M(~2d)US-14.01
US-14.2復帰検知→未送信記録をSupabaseへ自動同期(upsert冪等・同期キュー・リトライ)L(~3d)US-14.12,4
US-14.3復帰後に source_url付き記録へ /crawl抽出を起動し充填(ポーリング)M(~2d)US-14.23
US-14.4同期状態(未同期/同期中/済/失敗)をUIにバッジ表示S(~1d)US-14.15

❓ Open Q:画像のオフライン保持と復帰後アップロード(US-7.2と整合)。同期失敗の再試行上限/通知。複数デバイスはMVP対象か。

US-15:好み(焙煎度・フレーバー・産地)を登録・編集する P1 / Should

依存:US-11(認証) | 関連:US-8(初回キャプチャ)/ US-16(消費)/ §3.5 | ← 機能要件(一覧)に戻る

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)。

受け入れ条件

  1. 初回起動(US-8)と設定画面の両方から、好みを2-3タップ・スキップ可・具体選択で登録できる(惹かれる焙煎度/フレーバー/産地を選ぶ・§3.5 #3)。
  2. 設定画面から、好みを後追いで登録/編集できる(初回でスキップした人も後から設定可能)。
  3. 好みは「焙煎度の好み」「フレーバー(tasting note)の好み」「産地の好み」で表現する(§3.5 #5 焙煎度床+フレーバー照合)。挽き方は含めない(§3.5 #10)。
  4. 好みをリセット(クリア)でき、リセット後の推薦は属性近傍ではなく人気/季節へ優雅にフォールバックする(G-3 / S4)。
  5. 好み未設定(スキップ)でも記録・閲覧は妨げない。推薦はフォールバックを出す(背骨は記録のまま・§3.5 #1)。
  6. 好みは本人のみ参照・編集できる(user_preferences の RLS 本人限定)。
  7. (計測)申告好み vs 実記録の属性一致率を計測できる土台を持つ(§3.5 #8 副シグナル・E7)。

実装サブタスク

ID内容サイズ依存元AC
US-15.1user_preferences テーブル定義(user_id 1:1・roast_pref/flavor_pref/origin_pref・RLS本人限定)+マイグレーション(NOW)S(~1d)US-113,6
US-15.2好み入力UI部品(2-3タップ・具体選択・スキップ・フォールバック)を US-8 初回と設定で共用M(~2d)US-15.11
US-15.3設定からの好み登録/編集/リセット画面M(~2d)US-15.22,4
US-15.4申告好み vs 実記録の属性一致率の計測土台(US-16と共通・US-9基盤・§3.5 #8)S(~1d)US-15.1, US-95,7

出典:レコメンド OST §3.5「コールドスタート好みレコメンド v1」(grilling 2026-06-21)。最大の生死リスク=「初回の申告好みが、愛好家が実際に動く豆を予測できる」(E7 の主検証)。崩れたら自動化に進まず、マッチングか入力 UX を直す。

US-16:ホーム「あなたにおすすめの豆」で好みに合う今買える豆と出会う P1 / Should NOW・手動シード(WoZ)

依存:US-15(好み), catalog_beans(手動シード) | 関連:US-8(初回)/ §3.5・E7 | ← 機能要件(一覧)に戻る

NOTE:§3.5「コールドスタート好みレコメンド」の常設面。社会的発見の共起(UC1-1/1-2)とは別軸で、他ユーザーのデータ密度(C2)を待たず本人の申告好み × 手動シードのカタログで動く。リリースは手動キュレーシード=そのまま E7(申告好み検証)の Wizard of Oz。自動化(カタログ自動クロール)は §3.5 #9 ゲート通過後=NEXT。

Description

愛好家として、毎回の起動でホーム「あなたにおすすめの豆」から、自分の好みに近く・今買える・まだ飲んでいない豆と出会いたい。好み一致を床に、未踏の発見を上乗せして、記録 or 送客に着地できるように。

受け入れ条件

  1. ホームに「あなたにおすすめの豆」枠を常設し、user_preferences × catalog_beans(在庫検証済み「今買える」)を照合して提示する(§3.5 #5/#7)。
  2. 各カードは推薦理由(例「浅煎り × フルーティ」「未踏の産地で冒険」)と在庫バッジ(今買える/残りわずか)を示す(説明可能UX)。
  3. カードから記録(catalog_bean_id 紐付け)または送客(source_url)ができる。
  4. 好み未設定/カタログ薄では人気・季節へ優雅にフォールバックし、枠を空にしない(G-3)。挽き方はマッチングに使わない(§3.5 #10)。
  5. 好みチップ(焙煎度/フレーバー)で絞り込み・微調整できる(US-15 の好みと連動)。
  6. (計測)推薦→記録/送客の転換率、申告好み 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.01,4
US-16.2ホーム「あなたにおすすめの豆」UI(推薦カード・理由文・在庫バッジ・好みチップ・空状態フォールバック)M(~2d)US-16.12,4,5
US-16.3推薦→記録/送客(catalog_bean_id アトリビューション)+転換率・一致率の計測(US-9基盤・§3.5 #8/E7)M(~2d)US-16.2, US-93,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 内のみに置きクライアントへ出さない。

flowchart TB subgraph Client["Expo (React Native + TS)"] WV[react-native-webview] UI[記録UI / RHF+Zod] Q[TanStack Query
楽観更新・ポーリング] 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
図5. システムアーキテクチャ — 抽出はサーバ側に集約、アプリは閲覧と記録UIに専念

技術スタック(要点)

レイヤー採用主因
アプリ基盤Expo (React Native) + EAS運用工数最小・OTA更新
言語/型TypeScript strict + Zod全境界で実行時検証+型推論
サーバ状態TanStack Query楽観更新・抽出ポーリングに最適
BaaS/認証/DBSupabase(Auth/Postgres/RLS/Edge/Storage/Realtime)認証委譲・1ベンダー集約
ページ抽出Edge Function → Cloudflare /crawlトークン秘匿・サーバ集約
計測PostHog(+ Postgres SQL)リテンション/ファネルが標準装備
E2EMaestroYAML・Expo相性・CI容易

要石スキーマ(1スキーマ3役)

PRD §7.3 の「1スキーマ3役」を共有 Zod パッケージで物理的に一元化する。これが抽出・記録・将来レコメンドを貫く単一ソース。

flowchart LR Z["packages/schema
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
図6. 要石スキーマの派生 — 型・抽出契約・フォーム・DBが単一ソースから波及

データモデル:豆記録(beans)の主なフィールド

フィールド型必須備考
namestring◯銘柄名
shopstring◯購入元(店名)
source_urlstring(url)–商品ページURL(再注文導線)
origin_country / origin_regionstring–生産国 / 地域・農園・精製所
varietystring–品種(例:ゲイシャ)
processenum–washed / natural / honey / anaerobic / other
roast_levelenum–light 〜 dark
roast_date / purchased_atdatepurchased_at ◯焙煎日 / 購入日(既定=当日)
weight_g / price_jpynumber–内容量(g) / 価格
tasting_notes / my_memostring–風味メモ / 自由メモ
image_urlstring–袋写真 / 商品画像

付随テーブル:extraction_logs(充足率・誤り率・レイテンシ・shop=OKRの抽出ガードレール用)。beans には status: 'extracting'|'ready'、source: 'web'|'photo'|'manual'。RLS は beans.user_id = auth.uid() のみ許可。

認証(自前実装しない)

sequenceDiagram participant U as ユーザー participant App participant SBAuth as Supabase Auth U->>App: 初回起動 App->>SBAuth: signInAnonymously() SBAuth-->>App: user_id(匿名) Note over App: 即・記録可能(登録ゼロ摩擦) U->>App: 後でアカウント連携(任意) App->>SBAuth: linkIdentity(Apple / Google / Email OTP) SBAuth-->>App: 永続アカウント化
図7. 認証フロー — 匿名サインインで登録摩擦を回避し Activation を守る

出典:技術選定 ノート §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)に更新済み。

erDiagram users ||--o{ shops : "登録する" users ||--o{ beans : "記録する" users ||--o{ extraction_logs : "所有する" shops |o..o{ beans : "購入元(任意)" beans ||--o{ extraction_jobs : "抽出を起動" beans |o..o{ extraction_logs : "品質を計測" extraction_jobs |o..o{ extraction_logs : "結果を計測" catalog_sources ||--o{ catalog_items : "sitemapから発見(discover)" catalog_items ||--o| catalog_beans : "抽出成功時に掲載(harvest)" catalog_beans |o..o{ beans : "推薦元(任意・NEXT)" users ||--o| user_preferences : "申告好み" users { uuid id PK "auth.usersと1:1" text email UK "NOT NULL" text display_name "NULL可" timestamptz created_at "NOT NULL 既定now" } shops { uuid id PK "既定gen_random_uuid" uuid user_id FK "NOT NULL" text name "NOT NULL 店名" text url "NOT NULL 商品ページURL" text domain "NOT NULL 正規化ホスト" shop_capture capture_flag "NOT NULL 既定unknown" timestamptz created_at "NOT NULL 既定now" timestamptz updated_at "NOT NULL 既定now" } beans { uuid id PK "クライアント採番可 冪等upsert" uuid user_id FK "NOT NULL" uuid shop_id FK "NULL可 任意リンク" text name "NOT NULL 銘柄名" text shop "NOT NULL 店名 非正規化" text source_url "NULL可 再注文導線" text origin_country "NULL可" text origin_region "NULL可" text variety "NULL可 品種" process_type process "NULL可 enum" roast_level roast_level "NULL可 enum" date roast_date "NULL可 焙煎日" integer weight_g "NULL可 正数" integer price_jpy "NULL可 0以上" date purchased_at "NOT NULL 既定 当日" text tasting_notes "NULL可" text my_memo "NULL可" text image_url "NULL可 袋写真" visibility visibility "NOT NULL 既定private" bean_status status "NOT NULL 既定ready" bean_source source "NOT NULL 既定manual" uuid catalog_bean_id FK "NULL可 推薦元(NEXT)" timestamptz created_at "NOT NULL 既定now" timestamptz updated_at "NOT NULL 既定now" } extraction_jobs { uuid id PK "既定gen_random_uuid" uuid bean_id FK "NOT NULL" extraction_kind kind "NOT NULL page or photo" text input_url "NULL可 page時" text input_image_url "NULL可 photo時" text external_job_id "NULL可 crawl jobId" extraction_status status "NOT NULL 既定processing" jsonb result "NULL可 抽出JSON" text error_code "NULL可" timestamptz started_at "NOT NULL 既定now" timestamptz finished_at "NULL可" integer latency_ms "NULL可" } extraction_logs { uuid id PK "既定gen_random_uuid" uuid user_id FK "NOT NULL" uuid bean_id FK "NULL可" uuid extraction_job_id FK "NULL可" text shop "NULL可 集計軸" bean_source route "NOT NULL 経路別" smallint fields_total "NOT NULL" smallint fields_filled "NOT NULL 既定0" smallint fields_corrected "NOT NULL 既定0" numeric fill_rate "生成列 充足率" integer latency_ms "NULL可" timestamptz created_at "NOT NULL 既定now" } catalog_sources { uuid id PK "既定gen_random_uuid" text name "NOT NULL 店名" text site_url "NOT NULL" text sitemap_url "NOT NULL" text product_url_pattern "NOT NULL 商品URL判定RegExp" catalog_adapter adapter "NOT NULL 既定none" boolean enabled "NOT NULL 既定false" timestamptz last_discovered_at "NULL可" integer last_discovered_count "NULL可 急減検知用" text last_error "NULL可" timestamptz created_at "NOT NULL 既定now" timestamptz updated_at "NOT NULL 既定now" } catalog_items { uuid id PK "既定gen_random_uuid" uuid source_id FK "NOT NULL" text url "NOT NULL" text canonical_key UK "NOT NULL 正規化後の重複キー" catalog_item_status status "NOT NULL 既定pending" text reject_reason "NULL可 not_a_bean等" integer attempts "NOT NULL 既定0" text last_error "NULL可" timestamptz claimed_at "NULL可 processing失効判定用" timestamptz first_seen_at "NOT NULL 既定now" timestamptz last_seen_in_sitemap_at "NOT NULL 既定now" timestamptz created_at "NOT NULL 既定now" timestamptz updated_at "NOT NULL 既定now" } catalog_beans { uuid id PK "既定gen_random_uuid" uuid item_id FK "NOT NULL UNIQUE 抽出元台帳" text shop_name "NOT NULL 店名(非正規化)" text source_url "NOT NULL 送客先" text name "NOT NULL" text origin_country "NULL可" text origin_region "NULL可" text variety "NULL可" process_type process "NULL可 enum" roast_level roast_level "NULL可 enum" date roast_date "NULL可" integer weight "NULL可 正数" integer price "NULL可 0以上" text tasting_notes "NULL可 表示用" text image_url "NULL可" text flavor_tags "NOT NULL 既定{} 配列・最大3件" text origin_key "NULL可 産地正規化キー" boolean available "NOT NULL 既定true" timestamptz last_checked_at "NULL可" timestamptz created_at "NOT NULL 既定now 新着順の正本" timestamptz updated_at "NOT NULL 既定now" } catalog_state { boolean id PK "常にtrue・単一行" timestamptz cf_cooldown_until "NULL可" integer cf_cooldown_seconds "NOT NULL 既定600" timestamptz updated_at "NOT NULL 既定now" } user_preferences { uuid user_id PK "users と1:1・RLS本人のみ" roast_level roast_pref "NULL可 焙煎度の好み(配列)" text flavor_pref "NULL可 風味タグ照合(配列)" text origin_pref "NULL可 産地の好み(配列)" timestamptz created_at "NOT NULL 既定now" timestamptz updated_at "NOT NULL 既定now" }
図7. ER図 — 豆記録(beans)を中核に、ユーザー・対象店・抽出ジョブ・抽出品質ログが連なる。実線=必須FK(identifying)/破線=任意FK(NULL可)。推薦元 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_typewashed / natural / honey / anaerobic / otherbeans.process(PRD §7.3)
roast_levellight / medium-light / medium / medium-dark / darkbeans.roast_level(PRD §7.3)
visibilityprivate / publicbeans.visibility(US-12・既定private)
bean_statusextracting / readybeans.status(US-5 楽観更新の"解析中")
bean_sourceweb / photo / manualbeans.source / extraction_logs.route(US-9.3 経路別計測)
extraction_kindpage / photoextraction_jobs.kind(US-5=ページ、US-7=画像で別系統)
extraction_statusprocessing / done / failedextraction_jobs.status(US-5.2 非同期ポーリング)
shop_captureextractable / manual_recommended / unknownshops.capture_flag(US-10.6 robots判定)
catalog_adaptershopify_js / jsonld / nonecatalog_sources.adapter(§4.2・GitHub #175)
catalog_item_statuspending / processing / ready / rejected / delisted / failedcatalog_items.status(§7・GitHub #175)

users(アカウント) US-11

Supabase Auth が auth.users を管理し、本テーブルはその公開プロファイル(1:1)。自前パスワードは持たない。

列型NULLデフォルトキー / 制約・説明
iduuidNOT NULL—PK・FK → auth.users(id) ON DELETE CASCADE
emailtextNOT NULL—UNIQUE(auth 由来)
display_nametextNULL—表示名(任意)
created_attimestamptzNOT NULLnow()作成日時

shops(対象店) US-10

WebView閲覧(US-4)とページ抽出の対象店レジストリ。回遊・キュレーションは持たない最小設定。

列型NULLデフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
user_iduuidNOT NULL—FK → users(id) ON DELETE CASCADE
nametextNOT NULL—店名(US-10.1)
urltextNOT NULL—商品ページURL(US-10.2)
domaintextNOT NULL—正規化ホスト。WebViewホワイトリスト照合(US-10.5)
capture_flagshop_captureNOT NULL'unknown'robots判定で抽出可否(US-10.6 / US-6連携)
created_attimestamptzNOT NULLnow()
updated_attimestamptzNOT NULLnow()編集時更新(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デフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK。クライアント採番可(US-14 同期の一意性)
user_iduuidNOT NULL—FK → users(id) ON DELETE CASCADE。RLS本人のみ(US-11.5)
shop_iduuidNULL—FK → shops(id) ON DELETE SET NULL。撮影/手動記録は未設定可
nametextNOT NULL—銘柄名(必須)
shoptextNOT NULL—購入元店名(必須・非正規化/JSON Schema契約)
source_urltextNULL—商品ページURL(再注文導線・US-2.3)
origin_countrytextNULL—生産国
origin_regiontextNULL—地域 / 農園 / 精製所
varietytextNULL—品種(例:ゲイシャ)
processprocess_typeNULL—精製方法(enum)
roast_levelroast_levelNULL—焙煎度(enum)
roast_datedateNULL—焙煎日
weight_gintegerNULL—内容量(g)。CHECK(weight_g > 0)
price_jpyintegerNULL—価格(円)。CHECK(price_jpy >= 0)
purchased_atdateNOT NULLCURRENT_DATE購入日(必須・既定=当日/US-1 AC4)
tasting_notestextNULL—風味メモ
my_memotextNULL—自由メモ
image_urltextNULL—袋写真 / 商品画像(US-7.2 Storage)
visibilityvisibilityNOT NULL'private'公開設定の布石(US-12・既定非公開を保証)
statusbean_statusNOT NULL'ready'抽出中=extracting(US-5 楽観更新)
sourcebean_sourceNOT NULL'manual'記録経路(web/photo/manual・US-9.3)
catalog_bean_iduuidNULL—FK → catalog_beans(id) ON DELETE SET NULL。推薦経由の記録アトリビューション(§3.5・US-16)。NULL=通常経路
created_attimestamptzNOT NULLnow()一覧の新着順(US-2.1)
updated_attimestamptzNOT NULLnow()編集で更新(US-3)

extraction_jobs(抽出ジョブ) US-5 US-7

「記録を先に作る→裏で抽出」の非同期ジョブ(US-5.3)。ページ抽出(Cloudflare /crawl)と画像抽出(Vision LLM)を kind で1テーブルに統合。1豆が複数回の抽出を持ちうる(失敗→再試行)。

列型NULLデフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
bean_iduuidNOT NULL—FK → beans(id) ON DELETE CASCADE
kindextraction_kindNOT NULL—page=/crawl / photo=Vision(別系統)
input_urltextNULL—page 時の対象URL
input_image_urltextNULL—photo 時の画像
external_job_idtextNULL—Cloudflare /crawl の jobId(ポーリング用)
statusextraction_statusNOT NULL'processing'processing / done / failed(US-5.2)
resultjsonbNULL—抽出された構造化JSON(記載なしはnull固定・推測禁止)
error_codetextNULL—失敗理由(robots拒否 / timeout 等・US-6)
started_attimestamptzNOT NULLnow()ジョブ起動時刻
finished_attimestamptzNULL—完了 / 失敗時刻
latency_msintegerNULL—所要時間(10秒ガードレール監視・US-5.7)

extraction_logs(抽出品質ログ) US-9

抽出ガードレール(充足率80% / 誤り率 / レイテンシ)のPostgres真実源(US-9.4)。行動ファネル(Activation・リテンション)は PostHog(外部)で集計し、本テーブルは抽出品質の集計軸に専念する。

列型NULLデフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
user_iduuidNOT NULL—FK → users(id) ON DELETE CASCADE
bean_iduuidNULL—FK → beans(id) ON DELETE SET NULL
extraction_job_iduuidNULL—FK → extraction_jobs(id) ON DELETE SET NULL
shoptextNULL—店別集計軸(既存システム設計に準拠)
routebean_sourceNOT NULL—経路別計測(web/photo/manual・US-9.3)
fields_totalsmallintNOT NULL—スキーマ項目数(充足率の分母)
fields_filledsmallintNOT NULL0自動充填できた項目数
fields_correctedsmallintNOT NULL0ユーザー修正数(誤り率の分子)
fill_ratenumeric(4,3)NULL生成列GENERATED:fields_filled / NULLIF(fields_total,0)
latency_msintegerNULL—抽出レイテンシ
created_attimestamptzNOT NULLnow()計測時刻

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デフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
nametextNOT NULL—店名(catalog_beans.shop_name に書き写す)
site_urltextNOT NULL—サイトルート
sitemap_urltextNOT NULL—discover が読むsitemap
product_url_patterntextNOT NULL—商品URL判定用RegExp(Postgresでは使わない)
adaptercatalog_adapterNOT NULL'none'harvest の抽出方式
enabledbooleanNOT NULLfalse巡回対象に含めるか
last_discovered_attimestamptzNULL—直近のdiscover実行時刻
last_discovered_countintegerNULL—直近の発見件数。急減でパターン腐敗に気付く手掛かり
last_errortextNULL—直近のdiscoverエラー
created_attimestamptzNOT NULLnow()
updated_attimestamptzNOT NULLnow()

POST COFFEE / LIGHT UP COFFEE / LILO COFFEE ROASTERS / 森彦珈琲 の4店を enabled = true でseed済み。

catalog_items(クローラ内部台帳) NOW・#175

discover が発見した商品URLの状態機械。失敗状態・試行回数を持つためクライアントには一切公開しない(RLS有効・ポリシー無し・GRANT無し)。catalog_beans は本テーブルのready到達行から生成される表示専用の写像。

列型NULLデフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
source_iduuidNOT NULL—FK → catalog_sources(id) ON DELETE CASCADE
urltextNOT NULL—sitemap上のURL(生値)
canonical_keytextNOT NULL—UNIQUE。catalogKey(url)(§6.5)で正規化した重複キー
statuscatalog_item_statusNOT NULL'pending'discover〜harvestの状態
reject_reasontextNULL—not_a_bean / no_name 等
attemptsintegerNOT NULL0harvest再試行回数
last_errortextNULL—直近のharvestエラー
claimed_attimestamptzNULL—processing遷移時刻。10分超過で失効扱い(atomic claim)
first_seen_attimestamptzNOT NULLnow()台帳への初回登録時刻。claimの巡回順に使用
last_seen_in_sitemap_attimestamptzNOT NULLnow()直近のsitemapで確認できた時刻。delisted判定に使用
created_attimestamptzNOT NULLnow()
updated_attimestamptzNOT NULLnow()

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デフォルトキー / 制約・説明
iduuidNOT NULLgen_random_uuid()PK
item_iduuidNOT NULL—FK → catalog_items(id) ON DELETE CASCADE。UNIQUE(1台帳行につき最大1掲載)
shop_nametextNOT NULL—店名。catalog_sources.name を書き写した非正規化列(表示のたびにjoinしない)
source_urltextNOT NULL—商品ページURL(送客先)
nametextNOT NULL—銘柄名。カタログ内で唯一の必須項目
origin_countrytextNULL—生産国
origin_regiontextNULL—地域 / 農園 / 精製所
varietytextNULL—品種
processprocess_typeNULL—精製方法(beans と同一ENUM)
roast_levelroast_levelNULL—焙煎度(beans と同一ENUM)
roast_datedateNULL—焙煎日
weightintegerNULL—内容量(g)。CHECK(weight > 0)
priceintegerNULL—価格(円)。CHECK(price >= 0)
tasting_notestextNULL—風味メモ(表示用の自由文)
image_urltextNULL—商品画像URL。不正/相対URLでも行は落とさずclient側でnull化
flavor_tagstext[]NOT NULL'{}'フレーバータグ最大3件。CHECKは長さのみ・値域はDBで縛らない(zodで検証・spec D11)。user_preferences.flavor_pref と集合演算で照合
origin_keytextNULL—産地の正規化キー。user_preferences.origin_pref と照合
availablebooleanNOT NULLtrue在庫フラグ(一期一会フィルタ・§3.5 #2)。refresh が更新
last_checked_attimestamptzNULL—refresh が最後に在庫確認した時刻
created_attimestamptzNOT NULLnow()おすすめの新着順の正本(#198)
updated_attimestamptzNOT NULLnow()「最後に何か書いた時刻」。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デフォルトキー / 制約・説明
idbooleanNOT NULLtruePK。CHECK(id)で常に1行のみ許可
cf_cooldown_untiltimestamptzNULL—この時刻までharvestのCF呼び出しを止める
cf_cooldown_secondsintegerNOT NULL600次に429を受けたときの待ち時間(指数バックオフの現在値)
updated_attimestamptzNOT NULLnow()

user_preferences(申告好み) §3.5・NOW

コールドスタート好みレコメンド(§3.5「明示好み×カタログ」)の入力。ユーザーが初回オンボ/設定で申告する好みを 1ユーザー1行で保持し、catalog_beans(推薦元・店所有)と照合して「好み一致を床に、未踏の豆を上乗せ」する(§3.5 #5/#7)。挽き方は持たない(§3.5 #10)。RLS は beans と同じく本人のみ。

列型NULLデフォルトキー / 制約・説明
user_iduuidNOT NULL—PK・FK → users(id) ON DELETE CASCADE。1ユーザー1行(1:1)・RLS本人のみ
roast_prefroast_level[]NULL—焙煎度の好み(複数可・beans と同一ENUM)。フレーバー床(§3.5 #5)
flavor_preftext[]NULL—フレーバーの好みタグ。catalog_beans.flavor_tags と集合演算で照合(§8.2)
origin_preftext[]NULL—産地(国/地域)の好み(複数可)。catalog_beans.origin_key と照合
created_attimestamptzNOT NULLnow()
updated_attimestamptzNOT NULLnow()編集・リセットで更新(US-15.3)

全列 NULL 可=スキップ/未設定を許容(§3.5 #3)。未設定時はコールドスタート推薦が人気/季節へフォールバック(G-3)。flavor_pref の物理表現(text[] かタグ正規化テーブル)は実装時の設計余地。挽き方は持たない(§3.5 #10)。

リレーション一覧

親子多重度FK列ON DELETE根拠
usersshops1 — 0..Nshops.user_idCASCADEUS-10
usersbeans1 — 0..Nbeans.user_idCASCADEUS-11.5
usersextraction_logs1 — 0..Nextraction_logs.user_idCASCADEUS-9
shopsbeans0..1 — 0..Nbeans.shop_idSET NULLUS-4 / US-1(任意)
beansextraction_jobs1 — 0..Nextraction_jobs.bean_idCASCADEUS-5 / US-7
beansextraction_logs0..1 — 0..Nextraction_logs.bean_idSET NULLUS-9.4
extraction_jobsextraction_logs0..1 — 0..Nextraction_logs.extraction_job_idSET NULLUS-9.4
catalog_sourcescatalog_items1 — 0..Ncatalog_items.source_idCASCADEdiscover(§5・GitHub #175)
catalog_itemscatalog_beans1 — 0..1catalog_beans.item_idCASCADEharvest(§5・GitHub #175)
catalog_beansbeans0..1 — 0..Nbeans.catalog_bean_idSET NULL§3.5(推薦→記録アトリビューション・US-16/NEXT)
usersuser_preferences1 — 0..1user_preferences.user_idCASCADE§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-4WebView(対象店のみ)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-12visibility布石(既定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)
スコープ外(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案

#アイデア要点
1item-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 コンシェルジュMVPWizard of Oz(手動推薦)購入意向がベースライン比+30% / 送客クリック≥1/人
E4 visibilityスキーマ内蔵技術タスク(UI非表示の布石)OMTMに悪影響なく将来をマイグレ無しで開ける
flowchart LR E2["E2 インタビュー/観察"] --> E1["E1 データ監査
生死判定ゲート"] 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
図8. ディスカバリーの検証ゲート — E1(生死判定)通過時のみ E3 へ

出典:ディスカバリープラン - 社会的発見 ノート(要約)

ユースケース(社会的発見) 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化/匿名性リスクのため今回スコープ外。将来「個人を出さない集約通知(あなたの好み圏で人気上昇中の豆)」として再検討する。

各UCの詳細(アクター/トリガー/基本フロー/代替・例外/事後条件)と横断ガード G-1〜G-5は、サイドバー「ユースケース(社会的発見)」配下の個別ページに展開しています。

出典:ディスカバリープラン - 社会的発見 を起点に、ブレインストーミング(2026-06-21)で新規設計

UC1-1:豆詳細で「この豆が好きな人は次にこれ」から次の豆に出会う NEXT・設計段階

← ユースケース(社会的発見・一覧)に戻る

アクター/モーメント愛好家・発見システム / 豆詳細を見ている時
依存〔前提/実験〕C2・C3・#1購入 / E1・E3
事前条件対象豆に k-匿名性を満たす匿名共起データが存在(G-2)
トリガー利用者が豆詳細を開く

基本フロー

  1. 利用者が豆詳細を開く
  2. システムが「この豆を記録した人が他に記録した豆」を集計取得 → k-匿名性フィルタ+購入可能性で再ランク(G-2・G-3)
  3. 「この豆が好きな人は次にこれ」枠に上位N件を表示(出所は「みんなの記録から」=個人名なし/G-4)
  4. 利用者が候補をタップ → 候補豆の詳細
  5. 気に入れば記録、またはショップ商品ページへ(送客)

代替・例外

  • 代替 3a:購入可能な候補が乏しい → プロファイル共起(産地/焙煎度が近い豆)に切替(G-3)
  • 例外 2a:共起がk-匿名性未満/データ不足 → 枠を出さずアルゴ・回遊にフォールバック(G-1)

事後条件:新規記録(=出会い)または送客クリック。共起経由の新規記録率を計測。

UC1-2:記録直後に関連する共起豆をサジェストされる NEXT・設計段階

← ユースケース(社会的発見・一覧)に戻る

アクター/モーメント愛好家・発見システム / 記録した直後
依存〔前提/実験〕C2・C3 / E1・E3
事前条件記録が保存完了し、その豆にk-匿名性を満たす共起がある
トリガー記録の保存完了(楽観更新の確定後)

基本フロー

  1. 記録完了画面を表示
  2. 「この豆が好きな人は次にこれ」を1〜数件だけ提示(G-2・G-3適用)
  3. タップ → 候補詳細 → 記録 or 送客

代替・例外

  • 例外 2a:データ不足 → 何も出さない(記録直後の摩擦を増やさない=OMTM保護/G-1)

事後条件:連続的な発見、出会い数の増加。

UC2-1:好みが近い人(匿名)の高評価豆から出会う NEXT・設計段階

← ユースケース(社会的発見・一覧)に戻る

アクター/モーメント愛好家(データ充実)・発見システム / 次に何を買うか迷う時
依存〔前提/実験〕C2・C3・#2「刺さる」・#2「少データ近傍」 / E1・E3
事前条件嗜好ベクトルが算出できる最小記録数を満たす/近傍ユーザー群がk-匿名性を満たす(G-2)
トリガー「あなたへの発見(好みが近い人)」枠を開く

基本フロー

  1. 利用者が発見枠を開く
  2. システムが嗜好ベクトル近傍(テイスト・ツイン)をk人以上集約 → 高評価豆を匿名集計
  3. 「好みが近い人がよかった豆」を購入可能性で再ランクし提示(個人プロフィール/名前なし・嗜好グラフのみ/G-4)
  4. タップ → 詳細 → 記録 or 送客

代替・例外

  • 代替 3a:購入可能候補が乏しい → プロファイル軸(産地/焙煎度傾向)のおすすめに切替(G-3)
  • 例外 2a:記録が浅く近傍が定まらない → 「もう少し記録すると精度が上がる」空状態+共起/アルゴへフォールバック(G-1)
  • 例外 2b:近傍がk-匿名性未満 → 出さない(G-2)

事後条件:出会い(新規記録)・送客。ツイン経由の出会い率を計測。

UC3-1:自分の記録を匿名で「発見プールに提供」する(オプトイン) NEXT・設計段階

← ユースケース(社会的発見・一覧)に戻る

アクター/モーメント公開提供者・発見システム / 価値を実感した後
依存〔前提/実験〕C2(供給)・#3公開意向 / E2・E4
事前条件既定はすべて非公開(G-5)
トリガー設定で「発見に役立てる(匿名で提供)」をオン

基本フロー

  1. 何が共有されるかを明示(匿名の集計のみ・個人を特定しない)
  2. 同意 → 以降(+必要なら既存)の記録が visibility=公開で集計対象に
  3. 確認トースト「あなたの記録が匿名でみんなの発見に役立ちます」

代替・例外

  • 代替 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-2k-匿名性n人以上(例 n≥5)が記録した豆/ペア/近傍のみ提示。少人数での個人特定を防ぐプライバシー
G-3一期一会フォールバック購入可能性で再ランクし、買えない豆は順位を下げる/「ショップ・産地・焙煎度プロファイル」共起に切替#1購入 / E1(購入可能率)
G-4SNS化回避ガードフォロー/フィード/コメント/個人プロフィール公開を持たない。出所は「みんなの記録から」等の匿名集約表記のみ§5 トレードオフ
G-5プライバシー既定は非公開、共有は匿名集計のみ、撤回は即時反映E4 / 公開意向 E2

出典:ディスカバリープラン - 社会的発見 を起点に、ブレインストーミング(2026-06-21)で新規設計

リスクと未解決の問い

主要リスク

High記録が習慣化しない(最大リスク)
「手間>価値」になるとデータもレコメンドも成立しない。→ 入力摩擦を極限まで下げる(楽観更新・抽出・撮影・匿名サインイン)。NOWのOKRはこれを数字で証明することに全振り。
High記録密度・ユーザー間の重なり不足(社会的発見 C2)
各自バラバラな豆を買う愛好家は重なりが薄く、共起もツインも成立しない恐れ。→ E1(データ監査)を生死判定ゲートに。NEXT以降の前提。
Mediumページ抽出のカバレッジ/依存(H5・robots.txt)
robots.txt拒否店は抽出不可、非同期レイテンシ、Cloudflare+AI精度への依存。→ 着手前にH5スパイクで実測。未達なら撮影+手動主軸へ組み替え。
Medium送客マネタイズが立たない(戦略リスク3)
スペシャリティ領域はアフィリエイト網が薄い可能性。→ 早期にショップ1〜2社と送客実験を打診し、LATERの前提を先に潰す。
Medium一期一会で共起豆が買えない(社会的発見 #1)
出会わせても入手不可なら送客が成立しない。→ 共起対象を「豆」から「ショップ/産地/焙煎度プロファイル」へ切替する逃げ道を用意。
Medium豆マスタの整備コスト/精度(戦略リスク2)
構造化データが薄いとレコメンドが機能しない。→ クラウドソース+AI抽出の品質が鍵。要石スキーマを最初に丁寧に確定。

未解決の問い(Open Questions)

  • お気に入り店の robots.txt 許可割合は過半数に達するか(H5スパイク未実施)。
  • Flow B の Vision モデルは Workers AI で精度が足りるか、hosted vision が必要か(小スパイクで確定)。
  • スペシャリティ領域で送客アフィリエイトは経済的に成立するか。
  • 社会的発見の記録密度(C2)は閾値に達するか/ユーザーは記録を公開してよいと思うか。

前提(要バリデーション)

Assumptions(仮説)
  • 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色。各色に役割を固定し、増やさず使い切る。

Aa
primary#FF9D23主役・CTA/文字は濃色
Aa
secondary#5B7E3Cサブ操作・補助/文字は白
Aa
highlight#FFD65Aバッジ・装飾のみ
Aa
accent (error)#EA5252エラー/危険に一本化

primary スケール(state・背景tint用)

50
#FFF4E5
100
#FFE6C4
500
#FF9D23
600
#E58917
700
#C26F00

secondary スケール

50
#EEF3E6
100
#D8E4C4
500
#5B7E3C
600
#4C6B30
700
#3C5526
コントラスト(実測・WCAG AA = 4.5:1)
  • 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

background#FFFFFF
surface#FAF6F1
surfaceAlt#F2EBE2
border#E7DED2
text#2B201815.9:1
textSecondary#6F62565.9:1
textMuted#B8AC9E

Dark

background#17120E
surface#211A14
surfaceAlt#2B231C
border#3A3128
text#F6F0E816.4:1
textSecondary#B8AC9E8.4:1
textMuted#6F6256

3. カラー — セマンティック(状態色)

4色パレットを流用しつつ、出る文脈(トースト・インライン状態)で意味づけする。リンク/infoのみ暖色の外に置く(CTAとの誤認防止)。

トークンfilltext(淡背景上)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 GothicZenMaruGothic-Regular/-Medium/-Bold画面タイトル・セクション見出し
body(本文・UI)Zen Kaku Gothic NewZenKakuGothicNew-Regular/-Medium/-Bold本文・UI・数値・店名・ボタン・リンク
monoシステム等幅ui-monospace ほかコード・ID

タイプスケール(ThemedText の type と一致。weight は対応する weight 別ファミリで表現)

typesize / line-heightfamily(書体・weight)用途
title48 / 52Zen Maru Gothic Bold画面タイトル
subtitle32 / 44Zen Maru Gothic Mediumセクション見出し
default16 / 24Zen Kaku Gothic New Medium本文
small / smallBold14 / 20Zen Kaku Gothic New Medium / Bold補助・ラベル
link / linkPrimary14 / 30Zen Kaku Gothic New Regularリンク(color = info)
code12システム等幅(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 — 少数択はセグメント、多数択は選択チップ(焙煎度・精製など)。

ウォッシュト ナチュラル ハニー アナエロビック

カード/バッジ/リスト行/空状態

エチオピア イルガチェフェ NEW
¥1,580

フローラルで明るい酸。浅煎り・ウォッシュト。

☕
ケニア ニエリ中煎り・ONIBUS
抹茶
☕
グアテマラ中深煎り・LIGHT UP
記録済
🫘
まだ豆の記録がありません 最初の1袋を記録してみましょう

トースト — 状態色の 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)。