Feature: Trip Planner & Live Tracker (v0)
ID: 0001Status: In progress (M1–M9 shipped; M10–M11 remain)Owner: @satyaCreated: 2026-05-02Updated: 2026-05-12Related ADRs: 0001 (Flutter), 0002 (Supabase + NestJS), 0004 (monorepo), 0005 (SDD), 0006 (Docker/Coolify)Milestone tracker — see progress.md for live milestone status. Travelers (members + invites) shipped early as an out-of-sequence M11 slice ahead of M10 because spec 0003 needed share-with-collaborators to land first.
This is the first feature spec for Treeper. It covers what a user can do without any AI, community, or marketplace involvement: plan a trip on their phone and use it on the road. Pillars P1 and P2 from PRODUCT.md.
Subsequent specs (0002+) layer on AI ingest, communities, and the
marketplace.
1. Why
Section titled “1. Why”People plan trips across notes apps, screenshots, group chats, and inboxes. The plan goes stale the moment the trip starts because it lives in a place that doesn’t work offline or doesn’t update easily on the road.
We can fix that with one tightly-scoped, mobile-first product surface: a trip you can build in under a minute, edit anywhere, view offline, and actually use during travel.
2. Who it is for
Section titled “2. Who it is for”From PRODUCT.md §2:
- Solo planner (primary). One place for an upcoming trip.
- Group lead (primary). Builds the plan others will join later.
- Active traveller (primary). Uses the plan offline, marks things done.
Out of scope for this spec:
- Trip-mate seeker, inspiration hoarder, curated planner — served by later specs.
3. Scope
Section titled “3. Scope”3.1 In scope
Section titled “3.1 In scope”F1.1 — Trip CRUD
Section titled “F1.1 — Trip CRUD”- F1.1.a Create a trip (title, start date, end date, cover image optional).
- F1.1.b View trip overview (cover, dates, day count, activity count, total estimated cost).
- F1.1.c Edit trip metadata (title, dates, cover, currency, visibility).
- F1.1.d Delete a trip (soft-delete with 30-day undo, then hard-delete).
- F1.1.e Archive a trip (hidden from main list, still searchable).
F1.2 — Multi-destination
Section titled “F1.2 — Multi-destination”- F1.2.a Add one or more destinations to a trip (city / region / poi).
- F1.2.b Per-destination arrival and departure date (must fit inside trip dates).
- F1.2.c Reorder destinations.
- F1.2.d Single-destination trips remain a first-class case (no required multi-stop UI).
F1.3 — Day-by-day schedule
Section titled “F1.3 — Day-by-day schedule”- F1.3.a Days auto-generated from trip start/end dates.
- F1.3.b Empty days are valid (no required activity).
- F1.3.c Dates that change resize the schedule, preserving existing activities (clipped to new range; clipped activities go to a “needs re-scheduling” tray, never deleted).
F1.4 — Activity entries
Section titled “F1.4 — Activity entries”- F1.4.a Activity kinds:
transport,lodging,food,sight,freeform. Kind is mandatory. - F1.4.b Required fields: title, kind, day_id. Optional: time_start, time_end, location (lat/lng + label), notes (markdown), cost_amount, cost_currency.
- F1.4.c Per-day ordering: activities sort by
time_start ASC NULLS LAST, ties broken by user-set order index.
F1.5 — Drag-to-reorder
Section titled “F1.5 — Drag-to-reorder”- F1.5.a Reorder activities within a day.
- F1.5.b Move an activity from one day to another via drag.
- F1.5.c Reorder days within a trip (e.g. swap day 2 and 3).
F1.6 — Cost rollup
Section titled “F1.6 — Cost rollup”- F1.6.a Per-activity cost in any supported currency (ISO-4217).
- F1.6.b Day total = sum of activity costs converted to trip currency at the trip’s chosen rate snapshot.
- F1.6.c Trip total = sum of day totals.
- F1.6.d Per-traveller split (n equal shares) shown in summary.
F1.7 — Map view
Section titled “F1.7 — Map view”- F1.7.a Per-day map: pins for activities that have a location, ordered path between them.
- F1.7.b Full-trip map: all destinations + all activities.
- F1.7.c Tap a pin → activity detail.
F1.8 — Attachments and links
Section titled “F1.8 — Attachments and links”- F1.8.a Up to N photos per activity (initial limit: 8). Stored in Supabase Storage.
- F1.8.b One or more external URLs per activity, with a free-text label.
- F1.8.c File attachments (PDF tickets) — stretch; behind feature flag for v0.
F1.9 — Templates / duplicate
Section titled “F1.9 — Templates / duplicate”- F1.9.a Duplicate an existing trip into a new draft (same days, same activities, no live-mode log).
- F1.9.b “Save as template” stores a personal template under the user’s account; templates can seed new trips.
F1.10 — Local-first storage and sync
Section titled “F1.10 — Local-first storage and sync”- F1.10.a All reads on an opened trip work offline.
- F1.10.b Edits made offline queue locally and replay to the API on next network event.
- F1.10.c Conflict policy: last-write-wins per field at v0 (ADR follow-up for richer resolver later).
- F1.10.d Sync state visible in UI (synced / queued / conflict).
F1.11 — Privacy
Section titled “F1.11 — Privacy”- F1.11.a Visibility:
private(default),unlisted(link-only),public(discoverable later — placeholder UI for v0). - F1.11.b Changing visibility takes effect within 60 s, including for any cached share links.
F1.12 — Read-only share link
Section titled “F1.12 — Read-only share link”- F1.12.a Generating a link for an
unlistedtrip produces a stable URL that renders cover, days, activities — no editing. - F1.12.b Revoking the link invalidates it.
- F1.12.c Public web rendering is out of scope at v0; the link opens the app via deep-link and falls back to a minimal HTML preview hosted by the backend.
F2.1 — Live mode toggle
Section titled “F2.1 — Live mode toggle”- F2.1.a Per trip toggle between plan mode and live mode.
- F2.1.b Live mode default-shows “today” with the next-up activity at top.
- F2.1.c Auto-suggests live mode when current device time enters the trip date range.
F2.2 — Mark activity done
Section titled “F2.2 — Mark activity done”- F2.2.a Tick an activity as done; record actual start / end time (defaults to now).
- F2.2.b Optional actual cost overrides planned cost.
F2.3 — Inline log
Section titled “F2.3 — Inline log”- F2.3.a Photos attached to a done activity appear in an end-of-trip recap.
- F2.3.b Free-text journal field per activity.
- F2.3.c Voice memo — stretch, behind flag for v0.
F2.4 — Offline-first viewing
Section titled “F2.4 — Offline-first viewing”- F2.4.a Once a trip has been opened, all of its content is available offline until manually purged.
- F2.4.b Opened photos are cached; the user can pin a trip for “always available offline” with size warning.
F2.5 — Schedule drift
Section titled “F2.5 — Schedule drift”- F2.5.a When an activity’s actual_start exceeds planned_start by > 15 min, show a “running late” indicator.
- F2.5.b Offer a one-tap “shift remaining of today” that pushes all un-done activities back by the drift amount.
F2.6 — End-of-trip recap
Section titled “F2.6 — End-of-trip recap”- F2.6.a Auto-generated screen at trip end: cost vs planned, day-by-day photo strip, top-level metrics.
- F2.6.b Shareable as an image (out of scope at v0 — flagged).
3.2 Out of scope
Section titled “3.2 Out of scope”- AI ingest from social → spec
0002. - Prompt-to-itinerary → spec
0003. - Communities, open trips, trip-mates → spec
0004. - Trusted contacts → spec
0005. - Verified marketplace, day adventures → spec
0006. - Deals / price-watch → spec
0007. - Real-time collaboration on the same trip with multiple editors — defer to a later spec; v0 is single-author.
- Web client. Mobile only at v0.
4. User stories
Section titled “4. User stories”- As a solo planner, I can create a trip with a title, start date, and end date in under 30 seconds, so that my plan starts existing.
- As a solo planner, I can add activities to a day with a kind, title, and optional time/cost, so that the plan reflects what I intend.
- As a solo planner, I can drag activities to reorder them, so that the day matches reality.
- As a group lead, I can mark a trip as
unlistedand share a link, so that friends can see the plan without an account. - As an active traveller, I can view the entire trip offline once it has been opened, so that travel doesn’t break the app.
- As an active traveller, I can flip a trip into live mode and tick activities done, so that I have a journal at the end.
- As a solo planner, I can change a trip’s dates and have the schedule resize without losing activities, so that re-planning is safe.
5. UX notes
Section titled “5. UX notes”Screen names (subject to change in design pass):
TripList— home; user’s trips, separated by upcoming / past / archived.TripDetail— header + day tabs; floating ”+” for activity.ActivityEditor— bottom sheet, kind selector first.MapView— switchable per-day / per-trip.LiveDay— today view; large “next up” card; quick-tick.Recap— end-of-trip summary.
Interactions:
- Drag-and-drop reorder (long-press to grab).
- Pull-to-refresh in TripList triggers a sync.
- Sync state lives in a small chip in the TripDetail header.
This spec does not prescribe visual design; that is the next deliverable under the design system specs.
6. Acceptance criteria
Section titled “6. Acceptance criteria”Each criterion maps to one or more F-ids and must be testable.
AC-1 F1.1.a Given a signed-in user with no trips, when they tap "New Trip" and fill title + dates, then a trip exists in their account and appears in TripList.
AC-2 F1.1.d Given a trip exists, when the user deletes it, then it disappears from TripList immediately and is fully gone after 30 days; an "Undo" toast is shown for 10 s.
AC-3 F1.3.c Given a trip with activities on day 3, when the user shortens the trip to end before day 3, then the day-3 activities move to a "needs re-scheduling" tray and are not deleted.
AC-4 F1.4.a Given the activity editor is open, when the user attempts to save without selecting a kind, then save is blocked and a kind picker is shown.
AC-5 F1.5.b Given activities exist on day 1 and day 2, when the user drags activity X from day 1 onto day 2, then activity X belongs to day 2 and its order index is appended at the end of day 2's activities.
AC-6 F1.6.b Given activities with mixed currencies, when the user views a day total, then the total is shown in trip currency using the trip's stored rate snapshot, plus a footnote with the rate date.
AC-7 F1.7.a Given a day has at least two activities with locations, when the user opens the day map, then pins appear in chronological order with a visible connecting path.
AC-8 F1.10.a Given a trip has been opened on the device, when the device is offline, then the trip's content (text, last-cached photos) is viewable.
AC-9 F1.10.b Given the device is offline, when the user edits an activity title and reconnects, then the edit reaches Supabase and the trip on a second device reflects it within 30 s.
AC-10 F1.10.c Given two devices edit the same activity field while offline, when both come online, then the most recent edit by wall-clock time wins, and both devices show the same value within 60 s.
AC-11 F1.11.b Given a trip is `unlisted` with a share link, when the user changes visibility to `private`, then the share link returns 404 within 60 s.
AC-12 F2.1.a Given a trip's start date <= today <= end date, when the user opens the trip, then live mode is suggested by default.
AC-13 F2.2.a Given live mode is on for a trip, when the user ticks an activity done, then `actual_start` and `actual_end` default to "now" and are persisted.
AC-14 F2.5.a Given an activity's actual_start exceeds planned_start by more than 15 minutes, when live mode renders the day, then a "running late" indicator appears on the activity and a one-tap "shift remaining of today" CTA is offered.
AC-15 F2.6.a Given a trip whose end_date < today, when the user opens it, then the recap screen renders with cost-vs-planned, photo strip, and a list of completed activities.7. Data model (sketch)
Section titled “7. Data model (sketch)”Real schema lives in Supabase migrations. This is the v0 sketch.
users (managed by Supabase auth)trips id, owner_id, title, start_date, end_date, currency, cover_url, visibility, archived_at, deleted_at, created_at, updated_attrip_destinations id, trip_id, name, geo (lng,lat), arrival_date, departure_date, order_indextrip_days id, trip_id, date, day_indexactivities id, day_id, kind, title, time_start, time_end, location_label, location_geo, notes_md, cost_amount, cost_currency, order_index, planned_start, planned_end, actual_start, actual_end, done, created_at, updated_atactivity_attachments id, activity_id, kind (image/file), url, bytes, mime_type, order_indexactivity_links id, activity_id, url, labeltrip_share_links id, trip_id, slug, revoked_at, created_attrip_templates id, owner_id, source_trip_id, name, payload (jsonb)sync_journal (client-only; outbox of edits to replay)Notes:
actual_*columns kept besideplanned_*so live-mode timing isn’t destructive.deleted_atenables the 30-day undo window from F1.1.d.payloadontrip_templatesis the denormalised snapshot at save time.
8. APIs / contracts
Section titled “8. APIs / contracts”NestJS REST endpoints (mobile is the only first-party client). All require Supabase JWT.
POST /v1/tripsGET /v1/tripsGET /v1/trips/:idPATCH /v1/trips/:idDELETE /v1/trips/:id
POST /v1/trips/:id/destinationsPATCH /v1/trips/:id/destinations/:destIdDELETE /v1/trips/:id/destinations/:destId
POST /v1/trips/:id/days/:dayId/activitiesPATCH /v1/trips/:id/days/:dayId/activities/:actIdDELETE /v1/trips/:id/days/:dayId/activities/:actId
POST /v1/trips/:id/share-links (creates / rotates slug)DELETE /v1/trips/:id/share-links/:slug
POST /v1/uploads/sign (signed URL for Supabase Storage)Sync model:
POST /v1/sync/pull { since: cursor } -> { entities: [...], cursor }POST /v1/sync/push { ops: [{ entity, op, payload, client_ts }] }The mobile client treats /v1/sync/* as the only non-mutation read-source
once a trip is loaded.
9. Non-functional requirements
Section titled “9. Non-functional requirements”| Concern | Target |
|---|---|
| Cold start to TripList | < 1.5 s on a 2022-era mid-range Android. |
| Activity save (online) | < 300 ms p50 to ack from API. |
| Offline read | 100% of opened-trip content available offline indefinitely. |
| Sync replay | All queued ops drain within 30 s of regaining network. |
| Conflict | Last-write-wins per field; never silently drop a queued op. |
| Crash-free sessions | > 99.5% on mobile in beta. |
| Accessibility | All flows reachable via screen reader and 200% font scale. |
| Privacy | A private trip is never fetchable by any other user, even by guess. |
10. Risks & open questions
Section titled “10. Risks & open questions”- Q1. Currency rate source. Snapshot per-trip means stale rates on long-running trips. Decision: snapshot at trip create + manual refresh button. Re-evaluate before v1.
- Q2. Map provider. Options: Mapbox, Google, Apple. Cost + offline caching matter. ADR before implementation.
- Q3. State management library on Flutter. Riverpod is the working default; ADR if we deviate.
- R1. Live-mode timing UX needs user testing before we ship. Beta cohort.
- R2. Soft-delete with 30-day undo doubles row count on heavy churners; mitigate with periodic hard-delete job.
11. Rollout plan
Section titled “11. Rollout plan”- Branch. All work on
mainuntil first beta build is tagged. - Flags. F1.8.c (file attachments), F2.3.c (voice memo), F2.6.b (recap share image) ship behind feature flags off by default.
- Beta. Internal TestFlight + Play Internal track once AC-1 to AC-9 pass green.
- GA. When AC-1 to AC-15 pass and crash-free target is met for two consecutive beta builds.