# Yorvana — Specifications & Acceptance Criteria

> **Development cycle stage:** Step 1 of 3 — Specs/AC
> **Next steps:** Step 2 (ADR / Architecture), Step 3 (Implementation)

---

## 1. Overview

**Yorvana** is a mobile application for logging vehicle maintenance and repair history. It targets vehicle owners who want a reliable, personal record of everything done to their cars — without depending on a third-party service to store or own that data.

The defining principle is **user data ownership**: records are stored in an open, portable format on the user's device. Users can back up, sync, or migrate their data using any tool they choose — the app does not lock them in.

---

## 2. Target Users

- Individual vehicle owners managing 1 or more personal vehicles
- Users who want a digital replacement for a paper service book
- Users who value data portability and privacy over cloud-managed convenience

---

## 3. Goals

### MVP Goals
- Allow users to manage multiple vehicles, each with a detailed profile
- Allow users to create, view, edit, and delete service records per vehicle
- Support file attachments (receipts, invoices) on service records
- Store all data in an open, portable format that the user fully controls
- Deliver a modern, performant UI — responsive and non-sluggish

### Non-Goals (MVP)
- Cloud sync (planned post-MVP paid feature)
- In-app backup/restore (user manages this via the filesystem)
- Maintenance reminders / service interval alerts (post-MVP — see §8)
- Search and filtering of service records (post-MVP — see §8)
- iOS and Web platforms (post-MVP — architecture should not preclude them)
- Multi-user / account system

### Monetization Goals
- Freemium model: 1 vehicle free, unlimited vehicles via one-time lifetime purchase (see §5.7, ADR-012)

---

## 4. Platform

| Attribute | Value |
|---|---|
| Primary target | Android |
| Future targets | iOS, Web |
| Performance requirement | App must feel native and responsive — no perceptible lag on navigation, list rendering, or record creation |

---

## 5. Functional Requirements & Acceptance Criteria

### 5.1 Vehicle Management

**FR-V1: Add a vehicle**
- User can create a new vehicle profile with the following fields:
  - Nickname (required) — user-defined display name (e.g. "My Civic")
  - Make (optional) — searchable dropdown with common makes; custom values accepted
  - Model (optional) — free text
  - Year (optional) — 4-digit year
  - VIN (optional) — with a dedicated copy-to-clipboard action
  - License plate (optional)

**AC-V1:**
- [ ] Nickname is required and validated before saving; Nickname, Make, Model, and VIN are limited to 50 characters
- [ ] Year is validated only if provided (must be 1900–2100)
- [ ] Make field offers a filterable list of common manufacturers; user may type a custom value
- [ ] VIN field has a visible copy-to-clipboard button
- [ ] A successfully created vehicle appears in the vehicle list immediately
- [ ] No duplicate VINs allowed (if VIN is provided)

---

**FR-V2: View vehicle list**
- The app home screen shows all added vehicles
- Each vehicle card displays: nickname, make, model, year, and the date of the most recent service record (if any)

**AC-V2:**
- [ ] Vehicle list loads in under 300 ms for up to 20 vehicles on a mid-range Android device
- [x] "No vehicles yet" empty state is shown when the list is empty
- [ ] Vehicles are listed in user-defined order (order can be changed) or by most recently used

---

**FR-V3: Edit a vehicle**
- User can edit any field of an existing vehicle profile

**AC-V3:**
- [ ] Changes are persisted immediately on save
- [ ] Editing does not affect existing service records linked to the vehicle

---

**FR-V4: Delete a vehicle**
- User can delete a vehicle and all its associated service records and attachments

**AC-V4:**
- [x] A confirmation dialog is shown before deletion, clearly stating all records and attachments will be deleted
- [x] After deletion, the vehicle no longer appears in the list
- [ ] All associated files (records + attachments) are removed from storage

---

### 5.2 Service Record Management

**FR-R1: Add a service record**
- Within a selected vehicle, user can create a new service record with the following fields:
  - Date (required) — defaults to today
  - Odometer reading (required) — numeric, in km or miles (unit set at vehicle or app level)
  - Service type / category (required) — selected from a predefined list with an option to add custom categories
  - Performer (required) — who did the work; options: "Myself" or a named shop/mechanic (free text)
  - Cost (optional) — numeric with currency (currency set at app level)
  - Notes (optional) — free-text field for details
  - Attachments (optional) — one or more files (see §5.3)

**AC-R1:**
- [ ] Required fields are validated before saving; Performer, Work item names, and Work item details/fields are limited to 50 characters
- [ ] Odometer value must be between 0 and 9,999,999
- [ ] Date picker defaults to today
- [ ] Cost field accepts values up to 999,999.99 and is validated for max 2 decimal places
- [ ] Notes are limited to 1,000 characters
- [ ] Record appears at the correct chronological position in the list after save
- [ ] Saving a record with attachments correctly persists all attached files

---

**FR-R2: View service history**
- Within a vehicle, user sees a chronological list of all service records (newest first)
- Each list item shows: date, odometer, service type, performer, and cost (if set)

**AC-R2:**
- [ ] List renders without jank for up to 200 records
- [ ] Tapping a record opens a detail view with all fields and attachments
- [ ] "No records yet" empty state shown when there are no records

---

**FR-R3: Edit a service record**
- User can edit any field of an existing service record, including adding or removing attachments

**AC-R3:**
- [ ] Changes are persisted on save
- [ ] Removed attachments are deleted from storage
- [ ] Added attachments are stored and linked correctly

---

**FR-R4: Delete a service record**
- User can delete a service record

**AC-R4:**
- [ ] A confirmation prompt is shown before deletion
- [ ] All attachments linked to the deleted record are also deleted from storage
- [ ] Record is removed from the history list immediately

---

### 5.3 Attachments

**FR-A1: Attach files to a service record**
- User can attach one or more files to a service record
- Supported sources: device file picker (documents, images), camera capture

**AC-A1:**
- [ ] User can attach files from the system file picker
- [ ] User can attach a photo taken directly in-app via the camera
- [ ] Each attachment is displayed with its filename and a thumbnail (for images)
- [ ] Attachments are stored in a location accessible alongside the service data (open format, not app-private black-box storage)
- [ ] Tapping an image attachment opens the in-app full-screen image viewer; the viewer supports pinch-to-zoom (1×–5×), pan with bounds clamping, and animated double-tap to zoom in (2×, centered on tap point) / double-tap again to zoom back out
- [ ] Tapping a non-image attachment opens it using the system viewer

---

**FR-A2: Remove an attachment**
- User can remove an attachment from a record before or after saving

**AC-A2:**
- [ ] Removed attachment is deleted from storage when the record is saved
- [ ] UI updates immediately after removal

---

### 5.4 Service Categories

**FR-C1: Predefined service categories**
- The app ships with a default set of categories, including at minimum:
  - Oil Change
  - Tire Change / Rotation
  - Brake Service
  - Battery
  - Inspection / MOT / Technical Check
  - Fluid Top-up
  - Filter Replacement
  - Other

**FR-C2: Custom categories**
- User can add their own categories
- Custom categories are available across all vehicles

**AC-C1/C2:**
- [ ] Predefined categories are present on fresh install
- [ ] User can create and name a custom category (max 50 characters)
- [ ] Custom categories appear alongside predefined ones in the selector
- [ ] Categories are managed from a dedicated settings screen

---

### 5.5 Data Portability

**FR-D1: Open, portable data storage**
- All vehicle profiles, service records, and attachments are stored in an open format on the user's accessible filesystem (not in opaque app-private storage)
- The storage location (vault/folder) must be:
  - User-visible and navigable in a standard file manager
  - Portable — copying the folder preserves all data
  - Human-readable for the structured data (text-based format, not binary)

**AC-D1:**
- [ ] After creating a vehicle and records, the user can navigate to the data folder in a file manager and see the files
- [ ] The folder can be copied to another device or backed up with standard tools; the app can read it from the new location
- [ ] Structured data files (vehicle profiles, records) are text-readable without special tooling
- [ ] Attachment files are stored as-is (original format) in the same folder hierarchy

---

### 5.6 Settings

**FR-S1: Odometer unit**
- User can choose between km and miles at the app level (applies to all vehicles, or overridable per vehicle — TBD in ADR)

**FR-S2: Currency**
- User can set a default currency code used on cost fields
- A searchable list of common currency codes is provided; custom codes are also accepted

**FR-S3: Data folder location**
- User can view and (optionally) change the root folder where the app stores its data
- Default is a sensible location (e.g. a named folder in device storage)

**AC-S1/S2/S3:**
- [ ] Settings are persisted across app restarts
- [ ] Changing the data folder location prompts the user to move existing data or start fresh (behavior TBD in ADR)
- [ ] Odometer unit change is reflected across all existing records in the display

---

### 5.7 Premium / Paywall

**FR-P1: Free tier vehicle limit**
- Free users can manage exactly 1 vehicle with full read/write functionality (records, attachments, editing, etc.)
- Attempting to add a 2nd vehicle triggers an upgrade prompt
- If a vault contains more than 1 vehicle (e.g. selected an existing vault or vehicles added externally), the app enters read-only mode (see FR-P6)

**FR-P2: Premium upgrade**
- Premium is a one-time lifetime purchase via Google Play Billing
- After purchase, the user can manage unlimited vehicles
- Purchase persists across app reinstalls and device changes (Google Play handles this)

**FR-P3: Upgrade prompt**
- When a free user taps "Add Vehicle" with 1 vehicle already present, a dialog appears explaining the limit and offering to upgrade
- The dialog has an "Upgrade" action (launches Google Play purchase flow) and a "Not now" dismiss action
- After a successful purchase, the dialog auto-dismisses and the user is navigated to the Add Vehicle screen

**FR-P4: Restore purchases**
- User can restore a previous purchase from the Settings screen (e.g. after reinstall or device change)

**FR-P5: Premium status in Settings**
- The Settings screen shows a Premium section:
  - If not premium: displays upgrade prompt with "Upgrade" button and "Restore purchases" link
  - If premium: displays "Premium active" status

**FR-P6: Read-only mode for exceeded free tier**
- When a free-tier user's vault contains more than 1 vehicle, the entire app enters read-only mode
- A persistent warning banner is shown on all screens (vehicle list, vehicle detail, record list, record detail, settings)
- The banner explains the free-tier limit, suggests upgrading, and includes a button to navigate to the upgrade/purchase flow
- The user can view all vehicles and records but cannot create, edit, or delete anything
- Read-only mode is lifted immediately when the user upgrades to premium or the vault returns to ≤1 vehicle (e.g. vehicles removed externally)

**AC-P1:**
- [ ] With 0 vehicles, "Add Vehicle" navigates to the add screen without any prompt
- [ ] With 1 vehicle and no premium purchase, "Add Vehicle" shows the upgrade dialog
- [ ] With 1 vehicle and premium purchased, "Add Vehicle" navigates to the add screen
- [ ] Purchase flow launches correctly from the upgrade dialog
- [ ] Purchase flow launches correctly from the Settings screen
- [ ] After successful purchase, the upgrade dialog auto-dismisses and navigates to Add Vehicle
- [ ] After successful purchase, the Settings screen reflects "Premium active"
- [ ] "Restore purchases" in Settings correctly restores a previous purchase
- [ ] Premium status persists across app restarts (cached locally)
- [ ] When Google Play Billing is unavailable (offline, sideloaded), the app falls back to the locally cached premium status
- [ ] All premium-related strings are localized

**AC-P6:**
- [ ] With >1 vehicle and no premium, all create/edit/delete actions are disabled across the entire app
- [ ] Persistent warning banner is visible on vehicle list, vehicle detail, record list, record detail, and settings screens
- [ ] Warning banner includes an upgrade button that launches the purchase flow
- [ ] After successful upgrade, read-only mode is lifted immediately (no app restart needed)
- [ ] If vehicles are removed externally bringing count to ≤1, read-only mode is lifted on next vault refresh
- [ ] Read-only mode applies to all operations: vehicle CRUD, record CRUD, attachment add/remove

---

## 6. Non-Functional Requirements

| ID | Requirement |
|---|---|
| NFR-1 | App must be performant: navigation transitions ≤ 300 ms, list scrolling at 60 fps on a mid-range Android device |
| NFR-2 | Data is stored in open, text-readable format (specific format decided in ADR) |
| NFR-3 | No user account or internet connection required for any MVP feature |
| NFR-4 | App must not require special permissions beyond storage and camera |
| NFR-5 | Architecture must not prevent future iOS and Web support |
| NFR-6 | Cold start time ≤ 2 seconds on a mid-range Android device |

---

## 7. UX Principles

- **Modern design:** Clean, uncluttered UI. Follows platform conventions for Android (Material Design 3 or equivalent)
- **Non-sluggish:** Every interaction feels immediate. Skeleton/shimmer placeholders are shown while local data loads — never a blank screen or a blocking spinner
- **Progressive disclosure:** Show what matters first; details on demand
- **Destructive actions are guarded:** Deletions always require confirmation

---

## 8. Post-MVP Features (Out of Scope for Now — Spec for Reference)

### 8.1 Maintenance Reminders
- User can define a service interval per category per vehicle (e.g. oil change every 10,000 km or 12 months)
- App sends a local push notification when service is approaching (e.g. within 500 km or 1 month) or overdue
- A dedicated "Upcoming" or "Due Soon" view on the home screen

### 8.2 Search & Filter
- Search records by keyword (matches notes, performer, category)
- Filter records by date range, service category, or performer

### 8.3 Cloud Sync (Paid)
- Optional user-initiated sync to a cloud backend
- Does not replace local storage — cloud is a mirror, not the source of truth

### 8.4 iOS & Web
- Same feature set on iOS (native or cross-platform, per ADR)
- Web version (read-only or full, per ADR)

---

## 9. Open Questions for ADR (Step 2)

These are intentionally deferred to the architecture stage:

- What specific open format is used for structured data? (e.g. plain JSON files, YAML, Markdown with frontmatter)
- What is the folder/file structure of the vault?
- How are attachments named and organized relative to records?
- What cross-platform framework (if any) is used?
- Odometer unit: app-level setting only, or per-vehicle override?
- Behavior when the user changes the data folder location (migrate vs. fresh start)
- How is data integrity maintained if a file is manually edited outside the app?
