Perfect 👌 here's a Product Requirements Document (PRD) in Markdown, written so you can drop it straight into Lovable or v0 and start generating scaffolding from it.
⚠️ Historical Planning Document: This PRD reflects the initial project planning. Many features described here have been implemented, but implementation details differ from the original plan. For current architecture and implementation status, seeCLAUDE.mdandAGENTS.md. Notable changes: using Supabase exclusively (not Cloudflare KV/Neon), user profiles with sync keys are fully implemented, chat history and favorites systems are operational, and community reports with categories are live.
beta.rocks is a free, chat-first web app that helps climbers check real-time conditions at crags, sectors, and even specific routes. It combines forecast data (Open-Meteo), sun/shade (suncalc), crag/sector/route data (OpenBeta), and community reports. The app is local-first (works offline, stores data in local storage/IndexedDB) with optional sync across devices via a sync key.
- Provide a chat interface where users can ask about climbing conditions anywhere in any language.
- Allow users to post and confirm community reports about current conditions.
- Keep it free, privacy-respecting, and anonymous with optional display names.
- Work offline and sync data across devices.
- Be embeddable and shareable (links to crags/sectors/routes).
- Climbers: check if a crag/sector/route is dry, sunny, windy, crowded, etc.
- Community contributors: post quick reports (text, sliders, photos).
- Gyms/guides/blogs: embed a simple conditions widget.
- User types: “Siurana tomorrow?”, “El Pati shade this afternoon?”, “La Rambla dry?”.
- Powered by Gemini 2.5 Flash via Vercel AI SDK.
- Intent classification:
get_conditions(crag/sector/route + date)add_report(user report)confirm_report(thumbs up)search_crag/nearbyhelp
- Use existing
computeConditions()module (rule-based). - Inputs: Open-Meteo hourly forecast (temp, humidity, wind, rain, sun hours).
- Factors: recent rain, sun/wind exposure, rock type, sector aspect (via suncalc).
- Output: score + label (Great / OK / Meh / Nope) + reasons/tips.
- Base data: OpenBeta API.
- Hierarchy: Crag → Sector → Route.
- Allow “Add missing crag/sector/route” via community input.
- Store in local DB (IndexedDB), sync later.
-
Quick form:
- Dryness (1–5)
- Wind (1–5)
- Crowds (1–5)
- Optional text
- Optional photo
-
Reports can be attached to crag / sector / route.
-
Reports are stored locally, synced via sync key, and visible to all users.
- Any user can confirm an existing report.
- Confirms boost the report’s trust score.
- Schema:
reportId + userKeyHash. - Display: “👍 3 confirmed.”
- Each user has a sync key (random UUID/nanoid).
- Sync key allows multi-device sync (QR code or paste key).
- User can set an optional display name.
- Public: show displayName if set, else fallback to
Climber #abc. - Never expose raw sync keys.
-
/sync/:keyAPI endpoints:GET→ user’s data (crags, chats, reports).POST→ push merged data.
-
Conflict resolution via
updatedAt. -
Store in Cloudflare KV / Neon / Supabase.
-
Each crag/sector/route has a shareable URL:
beta.rocks/siurana/el-pati/la-rambla
-
Page shows:
- Current forecast summary.
- Community reports.
- “Last updated X minutes ago.”
- Show when forecast last updated.
- Show when each report was posted.
- No accounts required.
- Sync key acts as identity, but never displayed.
- Simple profanity filter on reports.
- Rate limiting by key/IP.
{
id: string; // OpenBeta ID or custom
name: string;
lat: number;
lon: number;
country: string;
rockType?: string;
aspects?: number[];
sectors?: Sector[];
}{
id: string;
cragId: string;
name: string;
lat?: number;
lon?: number;
aspect?: number;
routes?: Route[];
}{
id: string;
sectorId: string;
name: string;
grade?: string;
}{
id: string;
cragId: string;
sectorId?: string;
routeId?: string;
text?: string;
ratings?: { dry?: number; wind?: number; crowds?: number };
photo?: string;
authorName?: string;
createdAt: number;
updatedAt: number;
}{
reportId: string;
userKeyHash: string;
createdAt: number;
}{
syncKey: string;
displayName?: string;
createdAt: number;
updatedAt: number;
}- POST
{ message, lang, location?, state? } - Returns
{ reply, chips[], state } - Handles tool calling (get_conditions, add_report, confirm_report).
- POST new report.
- GET reports by crag/sector/route.
- GET user data.
- POST merged user data.
- GET crag conditions (Open-Meteo + computeConditions).
- Minimal UI: message list + input.
- Chips for quick actions.
- Display forecast summary + recent reports inline.
- Inline form (sliders + text + photo).
- Confirm button on each report.
- Show sync key + QR code.
- Input for display name.
- Button to reset data.
- Public view with conditions + reports for a crag/sector/route.
- Frontend: Next.js (Vercel), Vercel AI SDK, Tailwind, IndexedDB (local DB).
- Backend: Vercel Edge / Cloudflare Workers, Cloudflare KV or Neon DB.
- AI model: Gemini 2.5 Flash (via Vercel AI SDK).
- Data sources:
- Crags/sectors/routes: OpenBeta API.
- Weather: Open-Meteo.
- Sun position: suncalc.
- Chat-first interface.
- Crag-level conditions.
- Add & view reports.
- Confirm reports.
- Sync key (local + QR).
- Shareable links.