CLAUDE.md · git:20260417.ca34447 · 2026-04-17 · sha256 83c0c6bb6f97856e
CLAUDE.md git:20260417.ca34447A
Immutable. This exact content is served forever at /api/v1/blob/83c0c6bb6f97856e.
# Night Errand Runner — CLAUDE.md Master context for every Claude Code session. Read this before touching any code. --- ## What This App Is A night errand delivery web app for Novi Beograd, Belgrade, Serbia. A solo driver runs errands after midnight — pharmacy items, food, drinks, forgotten things — anything from anywhere that's open. Customers order via a mobile-first web app. Driver receives push, accepts, delivers. Customers get real-time status updates so they never have to call. **Brand positioning:** "Komšija koji ti pomaže" — the neighbour who helps. Warm, personal, human. Not a Glovo clone. --- ## Problem Being Solved > "Currently, a solo night errand operator in Novi Beograd is losing time and delivery capacity to unstructured order intake across WhatsApp, Viber, and phone — because there is no single place where customers place complete, structured orders and the driver manages them in one view." Full discovery docs in `/docs/`. --- ## Tech Stack — Pinned Versions (do not upgrade without explicit instruction) | Layer | Tool | Version | |---|---|---| | Framework | Next.js | 16.2.3 | | Language | TypeScript | 5.7.x | | UI Runtime | React | 19.x | | Styling | Tailwind CSS | 4.2.2 | | PWA | @serwist/next | 9.5.7 | | DB + Auth + Realtime | @supabase/supabase-js | 2.103.2 | | Supabase SSR | @supabase/ssr | latest compatible with 2.x | | Push notifications | firebase | 12.12.0 | | Maps | @vis.gl/react-google-maps | 1.x | | State management | zustand | 5.0.12 | | Hosting | Railway | — | **Version rules (mandatory):** - Use only the versions listed above. Do not upgrade silently. - If a library's documented API differs from what you know — trust the pinned version, not training data. - If a version conflict arises, stop and flag it. Do not resolve silently. - Tailwind v4 is a complete rewrite from v3. CSS-first config. Do not follow v3 patterns. - React 19: `createRoot` only. No `ReactDOM.render`. - Supabase JS v2: `createBrowserClient` / `createServerClient` from `@supabase/ssr` for Next.js App Router. --- ## Language **All UI copy, push notifications, error messages, button labels, and placeholder text must be in Serbian.** No English strings visible to users in production. Developer code (variable names, comments, API routes) stays in English. --- ## API Keys & Security (non-negotiable) - **Never expose API keys, tokens, or secrets on the client side.** - All calls to 3rd party APIs with credentials go through Next.js API routes (server-side). - `SUPABASE_SERVICE_ROLE_KEY` — server-side only. Never in any `NEXT_PUBLIC_` variable. - `FIREBASE_SERVICE_ACCOUNT` — server-side only. Never in any `NEXT_PUBLIC_` variable. - `GOOGLE_MAPS_API_KEY` — server-side only. A separate restricted key (`NEXT_PUBLIC_GOOGLE_MAPS_PUBLIC_KEY`) is used for the client-side map display only — restricted to Maps JS API + HTTP referrer. - Every secret goes in `.env.local` — verified in `.gitignore` before first commit. - Before any deploy: `grep -r "SERVICE_ROLE\|SERVICE_ACCOUNT" .next/` must return nothing. --- ## Non-Functional Requirements | NFR | Requirement | |---|---| | Availability | Occasional downtime tolerable at MVP. Railway free tier. | | Performance | LCP <2s mobile 4G. Form submit response <1s. Push delivery <5s. | | Security | HTTPS everywhere. Supabase RLS on all tables. Driver session expiry: 7 days. No PII in logs. | | Data residency | EU region. Supabase Frankfurt. Railway EU region. ZZPL (GDPR-equivalent) compliant. | | Deployment | Railway Next.js Node.js runtime. No Docker at MVP. | | Monitoring | Reactive. Railway log viewer. | | Push | FCM best-effort. Pull-to-refresh fallback on customer status screen. | | Privacy | No customer addresses or order contents in logs. No data shared between parties. `/privacy` page required. | --- ## Logging See `docs/logging-spec.md` for full spec. **Key rules:** - Log `orderId`, never order contents (item_description, address, etc. are PII-adjacent) - Never log: passwords, FCM tokens, IBAN, customer location, item descriptions - Format: structured JSON with `timestamp`, `level`, `event`, `requestId`, `orderId` - Production: INFO minimum. No `console.log` — use structured logger only. - Destination: Railway stdout → Railway log viewer --- ## Human-in-the-Loop Zones **Item substitution is always a human decision.** The driver calls the customer when an item is unavailable at the store. The app never automates this. No substitution logic, no suggestion engine, no automated alternatives. --- ## Database Schema See `docs/ai-spec-supabase.md` for full schema, RLS policies, and Supabase client setup. **Tables:** `orders`, `driver_profiles` **Auth:** Supabase Auth — driver only (email + password). Customers have no accounts. **Realtime:** Enabled on `orders` table for customer status screen subscription. --- ## Navigation Structure **Customer app:** Stack-only. No nav bar. - `/` — Order form - `/order/[id]` — Status screen - `/order/[id]/declined` — Declined screen - `/privacy` — Privacy policy **Driver app:** 2-tab bar (Novo/Aktivno | Istorija) - `/driver` — Login (redirect to `/driver/orders` if authenticated) - `/driver/orders` — Orders tab - `/driver/orders/[id]` — Order detail (accept/decline) - `/driver/orders/[id]/active` — Active order (status controls, store suggestions, IPS QR) - `/driver/history` — History tab (Post-MVP content) --- ## Sprint Sequence | Sprint | Goal | AI Spec | |---|---|---| | 1 | Foundation + Customer Order Form | `docs/ai-spec-sprint1.md` | | 2 | Driver Dashboard + Accept/Decline + Push Notifications | `docs/ai-spec-sprint2.md` | | 3 | Active Order + Store Suggestions + IPS QR | `docs/ai-spec-sprint3.md` | | 4 | Polish + PWA + Railway Deploy + Launch | `docs/ai-spec-sprint4.md` | **Rule:** Do not start Sprint N+1 before Sprint N is working end-to-end. --- ## Integration Specs | Integration | Spec file | |---|---| | Supabase | `docs/ai-spec-supabase.md` | | Firebase FCM | `docs/ai-spec-firebase-fcm.md` | | Google Maps | `docs/ai-spec-google-maps.md` | | NBS IPS QR | `docs/ai-spec-nbs-ips-qr.md` | --- ## Analytics See `docs/analytics-spec.md`. GA4 via `@next/third-parties/google`. No PII in event parameters. Environment variable: `NEXT_PUBLIC_GA_MEASUREMENT_ID` --- ## Quality Gates (every sprint) - Bearer security scan — no Critical/High findings before merge - No secrets in client bundle (verify with grep before deploy) - Supabase RLS tested — customer cannot read another customer's order - Push notification tested on real Android device - All UI copy in Serbian — no English strings in production - Order state changes logged per `docs/logging-spec.md` - No `console.log` in production --- ## Environment Variables Master List ``` # Supabase NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= SUPABASE_SERVICE_ROLE_KEY= ← server-side only # Google Maps NEXT_PUBLIC_GOOGLE_MAPS_PUBLIC_KEY= ← restricted: Maps JS API + HTTP referrer only GOOGLE_MAPS_API_KEY= ← server-side only: Geocoding + Places # Firebase NEXT_PUBLIC_FIREBASE_API_KEY= NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN= NEXT_PUBLIC_FIREBASE_PROJECT_ID= NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID= NEXT_PUBLIC_FIREBASE_APP_ID= NEXT_PUBLIC_FIREBASE_VAPID_KEY= FIREBASE_SERVICE_ACCOUNT= ← server-side only (JSON string) # Analytics NEXT_PUBLIC_GA_MEASUREMENT_ID= ``` All go in `.env.local` (not committed) AND Railway environment variables. --- ## CRISP Output Manifest All discovery and spec documents live in `docs/`: | File | Phase | Contents | |---|---|---| | `problem-statement.md` | C | Problem, constraints, NFRs, Go/No-Go | | `buy-vs-build-matrix.md` | C | Tool decisions | | `market-research.md` | C | TAM, competitors, USP | | `swot.md` | C | Strengths, weaknesses, opportunities, threats | | `value-proposition-canvas.md` | C | USP and positioning | | `decisions.md` | C+R+I+S | All key decisions | | `stakeholder-register.md` | R | Stakeholders, HITL zones | | `success-metrics.md` | R | Baseline + targets | | `process-flow.md` | I | AS-IS and TO-BE flows | | `user-journey-map.md` | I | Customer + Driver journeys | | `project-goals.md` | I | Goals, non-goals, success criteria | | `ux-discovery.md` | I | Mental models, visual direction, navigation, screens | | `design-system.md` | S | Colors, typography, components | | `ux-spec.md` | S | Sitemap, flow specs, screen specs | | `initial-backlog.md` | S | All stories with MVP tags | | `assumptions-log.md` | S | Assumptions and risks | | `risk-assessment.md` | S | Risk register | | `mvp-prioritization.md` | S | HVLE scoring, MVP line | | `logging-spec.md` | S | Logging rules | | `analytics-spec.md` | S | GA4 events | | `landing-page-brief.md` | S | Marketing copy | | `sprint-plan.md` | S | Sprint sequence and goals | | `ai-spec-sprint1.md` | S | Sprint 1 AI spec | | `ai-spec-sprint2.md` | S | Sprint 2 AI spec | | `ai-spec-sprint3.md` | S | Sprint 3 AI spec | | `ai-spec-sprint4.md` | S | Sprint 4 AI spec | | `ai-spec-supabase.md` | S | Supabase integration spec | | `ai-spec-firebase-fcm.md` | S | Firebase FCM integration spec | | `ai-spec-google-maps.md` | S | Google Maps integration spec | | `ai-spec-nbs-ips-qr.md` | S | NBS IPS QR integration spec |