Skip to content

Instantly share code, notes, and snippets.

@pappu687
Created August 24, 2026 07:04
Show Gist options
  • Select an option

  • Save pappu687/031d70f158628df7147e8efb60a9ab46 to your computer and use it in GitHub Desktop.

Select an option

Save pappu687/031d70f158628df7147e8efb60a9ab46 to your computer and use it in GitHub Desktop.
Vehicle Inspection App

Vehicle Inspection Platform — Mobile App Technical Specification

Status: Implementation specification
Target: Flutter / Android
Backend: Laravel API
Web: Laravel + Inertia + React
Local database: SQLite via Drift
Object storage: S3-compatible
Primary mode: Offline-first
Initial scope: Inspection only; no OBD, invoicing, repair orders, inventory, scheduling, or iOS


1. Purpose

This document turns the agreed product architecture into an implementation-oriented specification for the Android mobile application.

The application is not a hard-coded Toyota 160-point checklist. It is a generic inspection client that receives versioned inspection templates from the backend and renders them dynamically.

The Toyota 160-point inspection is a reference/base template. The source contains vehicle identity fields, inspection date, mileage, RO number and inspection-sheet number, categorized checks, certification/signature areas, and item outcomes such as Meets Standards, Adjust/Repair and N/A. The source also contains specialized additional and hybrid checks. The mobile application should model these as configurable template behavior rather than hard-code them.

Source reference: Toyota 160-Point Quality Assurance Inspection Check Sheet, page 1.


2. Product Decisions Already Made

2.1 Platform

  • Multi-tenant platform.
  • Multiple workshops/vendors.
  • Multiple brands.
  • Multiple vehicle models.
  • Multiple inspection types.
  • One vehicle can have many inspections.
  • Multiple mechanics can inspect the same vehicle over time.
  • One mechanic can inspect the same vehicle multiple times.
  • Completed inspections are immutable.
  • Corrections/reinspections create new linked records.
  • Template versions are frozen for an inspection.

2.2 Mobile

  • Flutter.
  • Android only for V1.
  • Offline-first is mandatory.
  • Camera.
  • Gallery.
  • Video.
  • Multiple media per inspection item.
  • Photo annotation.
  • Arrows/circles.
  • Timestamp.
  • GPS where permitted/configured.
  • VIN watermark.
  • Manual vehicle data entry.
  • No automatic VIN recognition.
  • No number-plate recognition.
  • No OBD integration.

2.3 Backend-driven behavior

The backend/template controls:

  • Item type.
  • Pass/fail behavior.
  • N/A availability.
  • Measurement requirements.
  • Measurement unit.
  • Validation rules.
  • Required evidence.
  • Number of required photos.
  • Video allowance.
  • Customer visibility.
  • Conditional visibility.
  • Severity defaults.
  • Section ordering.
  • Item ordering.

3. Mobile Goals

The mobile application must allow an inspector to:

  1. Authenticate.
  2. Enter/select their workshop context.
  3. Browse vehicles.
  4. Create a vehicle.
  5. View vehicle history.
  6. Start an inspection.
  7. Select an inspection type/template.
  8. Download/freeze the template version.
  9. Perform the inspection offline.
  10. Enter pass/fail/N/A results.
  11. Enter measurements.
  12. Create findings.
  13. Add notes.
  14. Capture photos.
  15. Select photos from gallery.
  16. Capture video.
  17. Annotate evidence.
  18. Capture evidence metadata.
  19. Continue after app restart.
  20. Continue without internet.
  21. Queue synchronization.
  22. Synchronize when connectivity returns.
  23. Complete the inspection.
  24. See synchronization/completion status.

4. Explicitly Out of Scope for Mobile V1

Do not implement:

  • OBD/Bluetooth diagnostic integration.
  • VIN decoder.
  • Automatic VIN recognition.
  • Automatic number-plate recognition.
  • Customer signatures.
  • In-app payments.
  • Repair orders.
  • Invoices.
  • Parts inventory.
  • Workshop scheduling.
  • Customer chat.
  • iOS.
  • Advanced analytics.
  • Full workshop-management functionality.
  • Backend PDF generation inside Flutter.

The backend owns PDF generation.


5. Mobile Navigation

Recommended navigation:

Login
  |
  v
Home
  |
  +-- Vehicles
  |     |
  |     +-- Vehicle List
  |     +-- Create Vehicle
  |     +-- Vehicle Details
  |           |
  |           +-- Vehicle Information
  |           +-- Inspection History
  |           +-- Start Inspection
  |
  +-- Inspections
  |     |
  |     +-- Draft / In Progress
  |     +-- Completed
  |
  +-- Sync
  |
  +-- Profile / Settings

During an inspection:

Inspection Overview
       |
       v
Section List
       |
       v
Section
       |
       v
Inspection Item
       |
       +-- Result
       +-- Measurement
       +-- Finding
       +-- Notes
       +-- Evidence
       |
       v
Next Item

6. Home Screen

The home screen should prioritize inspection work.

Suggested content:

Good morning, John

Workshop: Downtown Motors

[ Continue Inspection ]

Today
  Draft inspections: 2
  Completed: 4
  Pending sync: 7

[ Vehicles ]
[ Inspections ]

Sync status
  7 items pending

The exact visual design can evolve, but the home screen must expose:

  • Active/draft inspections.
  • Quick vehicle access.
  • Sync status.
  • Current workshop.
  • User identity.

7. Vehicle List

Required functionality:

  • Search by VIN.
  • Search by registration number.
  • Search by number plate.
  • Search by owner name.
  • Filter by recent activity.
  • Open vehicle.
  • Create vehicle.

No automatic detection is required.

Example:

Search vehicles...

ABC123
2024 Toyota Corolla
VIN: XXXXXXXXX
Owner: Rahim Ahmed
Last inspection: 24 Aug 2026

8. Create Vehicle

Required fields:

Brand
Model
Year
Variant / Trim
Fuel Type

VIN
Registration Number
Number Plate

Owner Name
Owner Phone (if required by backend)

Optional fields should be controlled by backend configuration where appropriate.

Validation:

  • VIN required if the selected tenant/template requires it.
  • Number plate may be required.
  • Registration number may be required.
  • Year must be valid.
  • Do not attempt VIN decoding.

9. Vehicle Details

The vehicle details page should show:

2024 Toyota Corolla

VIN
XXXXXXXXXXXX

Number Plate
ABC-123

Owner
Rahim Ahmed

--------------------------------

[ Start New Inspection ]

Inspection History

24 Aug 2026
Pre-Sale Inspection
Inspector: John
Result: Attention Required

18 Jul 2026
Trade-In Inspection
Inspector: Sarah
Result: Pass

The history is read-only for completed inspections.


10. Inspection Creation Flow

Start from vehicle details.

Vehicle
  |
  v
Start New Inspection
  |
  v
Select Inspection Type
  |
  v
Available Templates
  |
  v
Template Version
  |
  v
Inspection Setup
  |
  v
Start

Inspection setup should collect:

  • Inspection type.
  • Template.
  • Template version.
  • Current mileage.
  • Optional internal reference / RO number if configured.
  • Inspector.
  • Start timestamp.

The selected template version becomes immutable for the inspection.


11. Inspection State Machine

Recommended states:

DRAFT
  |
  v
IN_PROGRESS
  |
  v
READY_FOR_REVIEW
  |
  v
COMPLETED

Additional state:

CANCELLED

Historical relationship:

COMPLETED
    |
    +-- superseded_by --> NEW INSPECTION

State rules

DRAFT

Can:

  • Open.
  • Continue.
  • Cancel.
  • Delete locally if it has never synchronized, subject to backend policy.

IN_PROGRESS

Can:

  • Continue.
  • Pause.
  • Save.
  • Capture evidence.
  • Complete.

READY_FOR_REVIEW

Optional state if manager review is enabled by tenant/template.

Can:

  • Review.
  • Return for correction before final completion.

COMPLETED

Cannot:

  • Edit.
  • Delete.
  • Replace silently.

Can:

  • View.
  • Generate/view report through backend.
  • Create reinspection/correction inspection.

CANCELLED

Read-only historical record if synchronized.


12. Inspection Overview

The inspector should see:

Used Car Inspection

2024 Toyota Corolla
VIN: XXXXXXXX

Mileage: 82,341 km

Progress
142 / 160

Sections
✓ Vehicle Identity
✓ Road Test
⚠ Brakes
○ Under Hood
○ Functional
○ Appearance

[ Continue Inspection ]

Progress should be based on required items, not merely the number of UI pages visited.


13. Section List

Every section should display:

  • Section title.
  • Completion count.
  • Failed count.
  • Required evidence count.
  • Section status.

Example:

Road Test
34 / 34
✓

Brakes
5 / 8
⚠ 2 findings

Under Hood
0 / 14
○

14. Inspection Item UX

An inspection item is a mini workspace.

Example:

--------------------------------
Front brake pad thickness

Minimum: 4 mm

[ PASS ] [ FAIL ] [ N/A ]

Measurement
[ 6.2 ] mm

Notes
[ Light wear, even across axle... ]

Evidence
[ Camera ] [ Gallery ] [ Video ]

2 photos attached

Previous inspection
7.0 mm
18 Jul 2026

[ < Previous ]       [ Next > ]
--------------------------------

Item behavior is backend-driven

The UI should render controls from the template item definition.

Never hard-code:

if item.id == "brake_pad_front"

Instead:

if item.input.type == "measurement"

15. Supported Item Types

Minimum V1:

pass_fail
pass_fail_na
measurement
number
text
selection

Potential future types:

multi_selection
date
time
signature
barcode
photo_only
video_only

Do not implement future types until required.


16. Result Model

A result can contain:

PASS
FAIL
N/A

Optional internal status:

NOT_STARTED
IN_PROGRESS
COMPLETED

Do not expose NOT_STARTED as an inspection result unless the template explicitly supports it.


17. Measurement Model

A measurement item should define:

{
  "input": {
    "type": "measurement",
    "unit": "mm"
  },
  "validation": {
    "minimum": 4
  },
  "result": {
    "type": "pass_fail"
  }
}

The app records:

value = 6.2
unit = mm

The backend/template determines whether this means PASS or FAIL.

The mobile application may display the derived result immediately, but the backend remains authoritative.


18. Findings

A failed item may create a finding.

Fields:

id
inspection_result_id
severity
title
description
recommendation
status
created_at

Example:

Severity: High

Front left brake pad

Measured: 2.8 mm

Recommendation:
Replace front brake pads.

The mechanic should be able to create or edit the finding while the inspection is in progress.


19. Notes

Notes can exist:

  • On inspection.
  • On section, if supported later.
  • On inspection item.
  • On finding.

V1 should prioritize item notes and finding descriptions.


20. Conditional Logic

Templates can contain conditions.

Example:

IF vehicle.fuel_type == "hybrid"
THEN show "Hybrid Components"

Another example:

IF item.sunroof == "yes"
THEN show "Sunroof operation"

The mobile renderer should evaluate conditions locally from the downloaded template and current inspection answers.

The backend must validate the final inspection against the same rules.


21. Evidence

Every evidence record belongs to:

inspection
inspection_result

Supported:

  • Photo.
  • Video.

Multiple evidence objects per item are allowed.

Example:

Inspection
  |
  +-- Result #47
        |
        +-- Photo 1
        +-- Photo 2
        +-- Video 1

22. Camera Flow

When the user taps Camera:

Open camera
   |
   v
Capture
   |
   v
Preview
   |
   +-- Retake
   |
   +-- Use photo
          |
          v
     Add metadata
          |
          v
     Store locally

Do not upload directly from the camera screen.

The local file must first become a durable media record.


23. Gallery Flow

Gallery
  |
  v
Select one or more
  |
  v
Preview
  |
  v
Confirm
  |
  v
Store locally

Multiple selection must be supported.


24. Video

V1 requirements:

  • Capture video.
  • Store locally.
  • Associate with inspection item.
  • Queue upload.
  • Display upload state.

Avoid complex video editing in V1.


25. Photo Annotation

Annotations should not destroy the original.

Store:

Original image
+
Annotation data

Example:

{
  "type": "circle",
  "x": 0.62,
  "y": 0.44,
  "radius": 0.12
}

Use normalized coordinates from 0.0 to 1.0.

Supported V1 tools:

  • Circle.
  • Arrow.
  • Optional freehand marker if implementation remains simple.

26. Media Metadata

Each media record should support:

local_id
server_id

inspection_id
inspection_result_id

type
local_path
remote_path

mime_type
file_size
width
height

captured_at

latitude
longitude

watermark_applied

sha256

upload_status
created_at
updated_at

GPS may be null when:

  • Permission is denied.
  • GPS is unavailable.
  • Template does not require it.

27. Watermark

The app should support a configurable watermark layer.

Minimum identity:

VIN
Inspection number / local inspection identity
Timestamp

Workshop branding may be added later.

Important:

  • Preserve original.
  • Create derived/watermarked output.
  • Do not destroy original evidence.

28. Local Storage Architecture

Use Drift over SQLite.

Suggested local tables:

local_workshops
local_users

local_vehicles

local_templates
local_template_versions
local_template_sections
local_template_items

local_inspections
local_inspection_results
local_findings
local_measurements

local_media
local_annotations

local_sync_operations

Every synchronized entity should have:

local_id
server_id
sync_status
created_at
updated_at

29. Sync Status

Recommended values:

LOCAL_ONLY
PENDING
SYNCING
SYNCED
FAILED
CONFLICT

The UI should expose meaningful status.

Example:

✓ Saved locally
↻ Syncing
✓ Synced
⚠ Sync failed

Do not expose raw technical errors to mechanics.


30. Sync Queue

Every server mutation should create a sync operation.

Example:

SyncOperation

id
operation_type
entity_type
entity_local_id
payload
attempt_count
status
last_error
created_at
updated_at

Example:

CREATE_INSPECTION
CREATE_RESULT
UPDATE_RESULT
CREATE_FINDING
CREATE_MEDIA
COMPLETE_INSPECTION

Use idempotency keys.

Example:

idempotency_key = UUID

The same operation can safely be retried.


31. Sync Flow

User action
   |
   v
SQLite transaction
   |
   +--> UI updates immediately
   |
   +--> SyncOperation created
                |
                v
          Connectivity?
          /          \
        No            Yes
        |              |
      Queue          Upload
                       |
                  Server accepts
                       |
                  Mark SYNCED

If connectivity disappears during upload, retry safely.


32. Sync Error Handling

Network failure

Keep operation queued.

Authentication expired

Keep local data. Request authentication again.

Do not delete local inspections.

Server validation failure

Mark operation as FAILED.

Show a human-readable action.

Duplicate request

Server returns existing result based on idempotency key.

App killed during sync

Operation remains in SQLite and resumes later.

Phone restarted

All pending work remains.


33. Inspection Completion Offline

The inspector must be able to complete an inspection without internet.

Flow:

Complete
   |
   v
Local validation
   |
   +-- missing required data?
   |       |
   |       v
   |     Show errors
   |
   v
Mark COMPLETED locally
   |
   v
Queue completion operation
   |
   v
Sync later

The server repeats validation when the completion operation arrives.


34. Required Evidence Validation

Example rule:

{
  "evidence": {
    "photo": "required_on_fail",
    "minimum": 2
  }
}

If FAIL is selected:

Photo count < 2
     |
     v
Cannot complete item

The exact rule comes from the template.


35. Local Template Storage

The mobile device must download a complete immutable template version.

Store:

template
template_version
sections
items
rules
conditional logic
evidence rules

Once an inspection starts:

inspection.template_version_id

must remain unchanged.

Do not silently update the template while an inspection is in progress.


36. Template Update Strategy

When the backend publishes a new version:

Server:
Template v4 published

Mobile may download v4.

Existing inspection:

Inspection #100
Template v3

continues with v3.

New inspection:

Inspection #101
Template v4

uses v4.


37. Authentication

Recommended:

  • Laravel Sanctum or equivalent token-based API authentication.
  • Secure token storage on Android.
  • Refresh/session mechanism as defined by backend.
  • Never store access tokens in plain SharedPreferences.
  • Logout must not destroy unsynchronized inspection data without explicit user confirmation.

38. Workshop Context

The authenticated user belongs to one or more workshops according to backend permissions.

If multiple workshop contexts are supported:

Select Workshop

must happen before vehicle/inspection data is loaded.

Tenant context must never be accepted blindly from arbitrary client input.

The server determines authorized tenant access.


39. API Contract

Use OpenAPI as the source of truth.

Minimum mobile endpoints:

POST   /api/mobile/login

GET    /api/mobile/me
GET    /api/mobile/workshops

GET    /api/mobile/vehicles
POST   /api/mobile/vehicles
GET    /api/mobile/vehicles/{id}

GET    /api/mobile/templates
GET    /api/mobile/templates/{id}

POST   /api/mobile/inspections
GET    /api/mobile/inspections/{id}

POST   /api/mobile/sync

POST   /api/mobile/media/presign
POST   /api/mobile/media/complete

POST   /api/mobile/inspections/{id}/complete

Exact URLs can be changed during backend implementation; the important requirement is that the API contract is documented before mobile integration becomes broad.


40. Media Upload Strategy

Prefer pre-signed S3-compatible uploads.

Flow:

Flutter
  |
  | request upload URL
  v
Laravel
  |
  | signed upload URL
  v
Flutter
  |
  | upload directly
  v
S3
  |
  | complete upload
  v
Laravel

This prevents large video/photo payloads from unnecessarily passing through the Laravel application server.


41. Security

Mobile must assume all local data is potentially sensitive.

Protect:

  • Authentication tokens.
  • Local database where practical.
  • Private media.
  • Report credentials.
  • User/session data.

Do not rely on the mobile client to enforce authorization.

The backend is authoritative.


42. Android Permissions

Potential permissions:

CAMERA
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION

Media access should use modern Android APIs appropriate to the minimum supported Android version.

Permissions must be requested only when needed.

If location is optional:

Permission denied
     |
     v
Continue inspection

If a template explicitly requires GPS:

Permission denied
     |
     v
Show requirement
     |
     v
Allow retry

43. Crash / Recovery Requirements

App killed

Resume inspection from local state.

Phone reboot

Resume local inspection.

No internet

Continue inspection.

Server unavailable

Continue inspection.

Upload fails

Keep media locally and retry.

Authentication expires

Preserve local work.

Device storage low

Warn user and prevent silent data loss.


44. Loading / Empty / Error States

Every major screen must define:

  • Loading.
  • Empty.
  • Error.
  • Offline.
  • Retry.

Examples:

No vehicles found.

[ Create Vehicle ]
You're offline.

Your inspection is saved on this device.
Sync failed.

Your work is safe and will be retried.

[ Retry now ]

45. Mobile Architecture

Recommended layers:

Presentation
    |
Application / Use Cases
    |
Domain
    |
Repositories
    |
+-----------+-------------+
|                           |
Local Data               Remote API
Drift                     Laravel

Do not allow UI widgets to call HTTP directly.


46. Suggested Flutter Project Structure

lib/

  core/
    error/
    network/
    storage/
    permissions/
    connectivity/
    security/
    routing/

  features/

    authentication/
      data/
      domain/
      presentation/

    vehicles/
      data/
      domain/
      presentation/

    templates/
      data/
      domain/
      presentation/

    inspections/
      data/
      domain/
      presentation/

    media/
      data/
      domain/
      presentation/

    sync/
      data/
      domain/
      presentation/

    reports/
      data/
      domain/
      presentation/

    settings/
      data/
      domain/
      presentation/

47. State Management

Use one consistent state-management approach across the app.

The specific package can be selected before implementation, but it must support:

  • Reactive UI.
  • Dependency injection.
  • Async state.
  • Offline database observation.
  • Testability.

Avoid mixing multiple state-management patterns.


48. Repository Interfaces

Example:

abstract class InspectionRepository {
  Future<Inspection> createDraft(CreateInspectionInput input);

  Stream<Inspection> watchInspection(String localId);

  Future<void> saveResult(
    String inspectionId,
    SaveInspectionResultInput input,
  );

  Future<void> completeInspection(String inspectionId);

  Future<void> syncInspection(String inspectionId);
}

The presentation layer should depend on interfaces, not Drift or HTTP directly.


49. Domain Entities

At minimum:

Workshop
User
Vehicle

Template
TemplateVersion
TemplateSection
TemplateItem

Inspection
InspectionResult
Finding
Measurement

Media
Annotation

SyncOperation

50. Local IDs vs Server IDs

Every local record needs a stable UUID.

Example:

local_id = 01J...
server_id = nullable

Before synchronization:

server_id = null

After synchronization:

server_id = 987

Never use auto-increment SQLite IDs as cross-device synchronization identity.


51. Date / Time

Store timestamps as UTC internally.

Display according to workshop/user timezone.

Capture:

created_at
updated_at
started_at
completed_at
captured_at
synced_at

Do not use device-local formatted strings as the canonical value.


52. GPS

Store:

latitude
longitude
accuracy
captured_at

If needed later:

altitude
heading
speed

Do not make GPS a global mobile requirement. It should be controlled per template/item or media rule.


53. Photo Processing Pipeline

Recommended:

Camera
  |
  v
Original file
  |
  +--> SHA-256
  |
  +--> Thumbnail
  |
  +--> Watermarked derivative
  |
  +--> Annotation derivative
  |
  v
Local media record
  |
  v
Upload queue

Keep originals.


54. Customer Visibility

The mobile app should not decide customer report content.

It only records:

item result
finding
measurement
media
notes

The backend/report profile decides:

customer_visible = true/false

This allows internal and customer reports to differ.


55. Testing Strategy

Every vertical slice must include:

Unit tests

  • Domain rules.
  • Measurement validation.
  • Conditional rules.
  • State transitions.
  • Sync operation generation.

Repository tests

  • Drift persistence.
  • API mapping.
  • Offline behavior.

Widget tests

  • Item rendering.
  • Result selection.
  • Validation states.
  • Error states.

Integration tests

At minimum:

Login
Create vehicle
Start inspection
Save result
Kill/restart app
Resume
Complete offline
Sync

56. First Vertical Slice

Do not start with all 160 points.

Build:

LOGIN
  ↓
HOME
  ↓
CREATE VEHICLE
  ↓
START INSPECTION
  ↓
ONE TEST TEMPLATE
  ↓
ONE SECTION
  ↓
ONE ITEM
  ↓
PASS / FAIL
  ↓
SAVE TO SQLITE
  ↓
KILL APP
  ↓
REOPEN
  ↓
RESUME

Acceptance criteria:

  • User can authenticate.
  • Vehicle can be created.
  • Test template is available locally.
  • Inspection can start.
  • Result saves to Drift.
  • App can be killed.
  • App can restart.
  • Inspection remains intact.
  • Result remains intact.
  • No network is required after template availability.

57. Vertical Slice 2 — Full Checklist Renderer

Implement:

  • Multiple sections.
  • Multiple items.
  • Section progress.
  • Item progress.
  • Pass/fail/N/A.
  • Text.
  • Number.
  • Measurement.
  • Conditional items.
  • Local persistence.

Acceptance criteria:

  • Template contains at least 3 sections.
  • At least 10 items render dynamically.
  • No item IDs are hard-coded in Flutter.
  • App works without network.
  • Inspection resumes after restart.

58. Vertical Slice 3 — Findings & Measurements

Implement:

  • Measurement fields.
  • Minimum/maximum validation.
  • Derived PASS/FAIL.
  • Finding creation.
  • Severity.
  • Recommendation.
  • Notes.

Acceptance criteria:

  • A measurement can automatically determine result.
  • Invalid values are clearly shown.
  • FAIL can create a finding.
  • Findings persist after restart.

59. Vertical Slice 4 — Photos

Implement:

  • Camera.
  • Gallery.
  • Multiple photos.
  • Local file persistence.
  • Item association.
  • Thumbnail generation.
  • Timestamp.
  • SHA-256.
  • Watermark.

Acceptance criteria:

  • Five photos can be attached to one item.
  • App restart does not lose them.
  • Original files remain available.
  • Thumbnail is shown immediately.
  • Upload is not required to continue inspection.

60. Vertical Slice 5 — Annotation

Implement:

  • Open image.
  • Draw circle.
  • Draw arrow.
  • Undo.
  • Clear.
  • Save annotation.
  • Render preview.

Acceptance criteria:

  • Original image remains untouched.
  • Annotation survives app restart.
  • Annotation uses normalized coordinates.
  • Derived image can be generated.

61. Vertical Slice 6 — Video

Implement:

  • Camera video capture.
  • Local persistence.
  • Thumbnail.
  • Duration.
  • Upload queue.

Acceptance criteria:

  • Video remains available offline.
  • Upload can be resumed after failure.

62. Vertical Slice 7 — Sync

Implement:

  • Connectivity detection.
  • Sync queue.
  • Idempotency keys.
  • Retry.
  • Exponential backoff.
  • Server acknowledgement.
  • Media upload.
  • Failure states.

Acceptance criteria:

Start offline
↓
Complete inspection
↓
Turn internet on
↓
Sync
↓
Refresh backend
↓
Inspection exists exactly once

63. Vertical Slice 8 — Real Backend Templates

Implement:

  • Laravel template API.
  • Template version download.
  • Local template cache.
  • Template activation.
  • Version freeze.

Acceptance criteria:

Backend v1
↓
Mobile downloads v1

Backend v2
↓
Mobile downloads v2

Existing inspection
↓
Still uses v1

New inspection
↓
Uses v2

64. Vertical Slice 9 — Vehicle History

Implement:

  • Vehicle history endpoint.
  • Local vehicle history.
  • Inspection list.
  • Inspection detail.
  • Reinspection relationship.

Acceptance criteria:

Vehicle A
  |
  +-- Inspection 1 — John
  +-- Inspection 2 — Sarah
  +-- Inspection 3 — John

All records remain individually identifiable.


65. Vertical Slice 10 — Completion

Implement:

  • Required-field validation.
  • Required-evidence validation.
  • Final local completion.
  • Server completion.
  • Immutable state.

Acceptance criteria:

  • Missing required fields block completion.
  • Required evidence blocks completion.
  • Completed inspection cannot be edited.
  • Offline completion is allowed.
  • Server revalidates completion.

66. Vertical Slice 11 — Production Hardening

Implement:

  • Error boundaries.
  • Crash-safe persistence.
  • Storage warnings.
  • Authentication expiry recovery.
  • Sync diagnostics.
  • Permission recovery.
  • Database migrations.
  • App upgrade migration tests.

67. AI Editor Execution Rules

For Codex/Antigravity:

  1. Work one vertical slice at a time.
  2. Do not implement future slices prematurely.
  3. Do not invent API contracts.
  4. Do not hard-code template item IDs.
  5. Do not hard-code Toyota-specific behavior.
  6. Do not bypass repositories.
  7. Do not put HTTP calls directly in widgets.
  8. Do not destroy local unsynchronized data.
  9. Do not mutate completed inspections.
  10. Write tests with every slice.
  11. Run formatter/analyzer/tests after each slice.
  12. Keep the application runnable after every slice.
  13. Prefer small commits.
  14. Document assumptions.
  15. Stop and ask for clarification when a product decision is genuinely missing rather than inventing business behavior.

68. Definition of Done for a Vertical Slice

A slice is complete only when:

Code implemented
+
Database migration complete
+
API contract updated if applicable
+
Tests added
+
Offline behavior tested where applicable
+
Error states handled
+
Loading states handled
+
No hard-coded business/template IDs
+
Flutter analyzer clean
+
Tests passing
+
Existing functionality still works

69. Recommended First Development Task

The first task for the AI editor should be:

Set up the Flutter Android application.

Implement:
- Project structure
- Environment configuration
- Routing
- Dependency injection
- Local Drift database
- Secure authentication storage
- Connectivity abstraction
- Basic error handling
- Test infrastructure

Do NOT implement:
- Real inspection templates
- Camera
- Sync
- PDF
- Media
- Backend integration beyond the authentication abstraction

Then build Vertical Slice 1.


70. Important Architectural Boundary

The mobile application is a client, not the authority.

Backend is authoritative for:

  • Users.
  • Permissions.
  • Tenant access.
  • Templates.
  • Template versions.
  • Validation.
  • Inspection completion.
  • Report visibility.
  • Audit.

Mobile is authoritative only for its temporary local state until synchronization.


71. Final Mobile Architecture

                 ┌─────────────────────┐
                 │       Flutter       │
                 │       Android       │
                 └──────────┬──────────┘
                            │
                 ┌──────────▼──────────┐
                 │    Presentation     │
                 │ Screens / Widgets   │
                 └──────────┬──────────┘
                            │
                 ┌──────────▼──────────┐
                 │ Application / Use   │
                 │      Cases          │
                 └──────────┬──────────┘
                            │
                 ┌──────────▼──────────┐
                 │       Domain        │
                 │ Inspection / Media  │
                 └──────────┬──────────┘
                            │
              ┌─────────────┴─────────────┐
              │                           │
       ┌──────▼──────┐             ┌──────▼──────┐
       │   Drift     │             │  Laravel    │
       │   SQLite    │             │     API     │
       └──────┬──────┘             └──────┬──────┘
              │                           │
       ┌──────▼──────┐             ┌──────▼──────┐
       │ Sync Queue  │────────────►│    MySQL    │
       └─────────────┘             └─────────────┘
                                         │
                                  ┌──────▼──────┐
                                  │ S3 Storage  │
                                  └─────────────┘

72. Final Readiness Checklist

Before starting broad mobile development, confirm these artifacts exist:

  • Product architecture document
  • Mobile technical specification
  • Final template JSON schema
  • OpenAPI backend contract
  • Drift local schema
  • Sync protocol
  • Media/annotation model
  • Inspection state machine
  • Screen/navigation specification
  • Vertical slice backlog
  • Test strategy
  • Android development environment
  • Laravel local development environment
  • S3-compatible development bucket
  • Test workshop
  • Test users for each role
  • Test vehicle
  • Test inspection template

Once those are in place, the project is ready to move from architecture into implementation.

@pappu687

Copy link
Copy Markdown
Author

MOBILE_DATA_MODEL.md

Vehicle Inspection Platform — Mobile Data Model

Target: Flutter / Android
Persistence: SQLite via Drift
Mode: Offline-first
Backend authority: Laravel API
Storage: S3-compatible object storage for remote media


1. Goals

The local database must:

  • Allow a mechanic to perform a complete inspection without internet.
  • Survive app termination and device restart.
  • Persist inspection results immediately.
  • Persist media before upload.
  • Queue all server mutations for synchronization.
  • Keep template versions immutable for active inspections.
  • Support multiple inspections for the same vehicle.
  • Support multiple mechanics over the vehicle's history.
  • Avoid duplicate records during retry.
  • Never lose unsynchronized work because authentication or connectivity failed.

The mobile database is a working copy, not the system of record. Laravel remains authoritative after synchronization.


2. Identity Rules

Every locally-created entity uses a UUID:

local_id: UUID
server_id: nullable UUID / backend identifier

Do not use SQLite auto-increment IDs as synchronization identity.

Recommended metadata on synchronized entities:

local_id
server_id
sync_status
created_at
updated_at

Suggested sync states:

LOCAL_ONLY
PENDING
SYNCING
SYNCED
FAILED
CONFLICT

3. Entity Relationship Overview

Workshop
   |
   +---- User
   |
   +---- Vehicle
            |
            +---- Inspection
                    |
                    +---- TemplateVersion
                    |
                    +---- InspectionResult
                              |
                              +---- Finding
                              |
                              +---- Measurement
                              |
                              +---- Media
                                      |
                                      +---- Annotation

SyncOperation
   |
   +---- any local mutation

4. Local Tables

4.1 local_workshops

Purpose: cache authorized workshop/tenant information.

Fields:

local_id              TEXT PK
server_id             TEXT UNIQUE
name                  TEXT
code                  TEXT NULL
status                TEXT
created_at            DATETIME
updated_at            DATETIME

Indexes:

server_id
name

4.2 local_users

local_id              TEXT PK
server_id             TEXT UNIQUE
workshop_id           TEXT
name                  TEXT
email                 TEXT
role                  TEXT
status                TEXT
created_at            DATETIME
updated_at            DATETIME

Foreign key:

workshop_id -> local_workshops.local_id

4.3 local_vehicles

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

workshop_id           TEXT

brand                 TEXT
model                 TEXT
year                  INTEGER NULL
variant               TEXT NULL
fuel_type             TEXT NULL

vin                   TEXT NULL
registration_number   TEXT NULL
number_plate          TEXT NULL

owner_name            TEXT NULL
owner_phone           TEXT NULL

status                TEXT

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Indexes:

workshop_id
vin
registration_number
number_plate
owner_name

Search should support partial matching where practical.


5. Template Storage

Templates are downloaded from the backend and cached locally.

5.1 local_templates

local_id              TEXT PK
server_id             TEXT UNIQUE

workshop_id           TEXT NULL
base_template_id      TEXT NULL

name                  TEXT
code                  TEXT NULL
inspection_type       TEXT

status                TEXT

created_at            DATETIME
updated_at            DATETIME

5.2 local_template_versions

local_id              TEXT PK
server_id             TEXT UNIQUE

template_id           TEXT
version_number        INTEGER

schema_version        TEXT

status                TEXT

published_at          DATETIME NULL

checksum              TEXT

created_at            DATETIME
updated_at            DATETIME

Unique constraint:

(template_id, version_number)

A template version is immutable after publication.


5.3 local_template_sections

local_id              TEXT PK
server_id             TEXT UNIQUE

template_version_id   TEXT

section_key           TEXT
title                 TEXT
description           TEXT NULL

sort_order            INTEGER

visibility_rule_json  TEXT NULL

created_at            DATETIME
updated_at            DATETIME

Unique:

(template_version_id, section_key)

5.4 local_template_items

local_id              TEXT PK
server_id             TEXT UNIQUE

template_section_id   TEXT

item_key              TEXT
title                 TEXT
description           TEXT NULL

input_type            TEXT

config_json           TEXT

validation_json       TEXT NULL
result_config_json    TEXT NULL
evidence_config_json  TEXT NULL
visibility_rule_json  TEXT NULL

sort_order            INTEGER

required              BOOLEAN

customer_visible      BOOLEAN

created_at            DATETIME
updated_at            DATETIME

config_json contains backend-defined item configuration.

Do not hard-code Toyota-specific item IDs in Flutter.


6. Inspections

6.1 local_inspections

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

workshop_id           TEXT
vehicle_id            TEXT

template_version_id   TEXT

inspection_type       TEXT

status                TEXT

lead_inspector_id     TEXT

mileage               INTEGER NULL

internal_reference    TEXT NULL

started_at            DATETIME
completed_at          DATETIME NULL

progress_total        INTEGER
progress_completed    INTEGER

overall_result        TEXT NULL
overall_score         REAL NULL

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Recommended indexes:

vehicle_id
workshop_id
status
lead_inspector_id
started_at

7. Inspection Results

7.1 local_inspection_results

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

inspection_id         TEXT
template_item_id      TEXT

status                TEXT

result                TEXT NULL
value_text            TEXT NULL
value_number          REAL NULL

notes                 TEXT NULL

failure_reason        TEXT NULL

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Recommended result values:

PASS
FAIL
NA
NULL

NULL means the item has not yet been answered. It is not an inspection outcome.


8. Measurements

8.1 local_measurements

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

inspection_result_id  TEXT

value                 REAL
unit                  TEXT

minimum               REAL NULL
maximum               REAL NULL

derived_result        TEXT NULL

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

The backend/template defines the authoritative validation rules.

Mobile may calculate a provisional result for immediate UI feedback.


9. Findings

9.1 local_findings

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

inspection_result_id  TEXT

severity              TEXT
category              TEXT NULL

title                 TEXT
description           TEXT NULL
recommendation        TEXT NULL

status                TEXT

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Suggested severity values:

INFO
LOW
MEDIUM
HIGH
CRITICAL

The actual set should be backend-configurable.


10. Media

10.1 local_media

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

inspection_id         TEXT
inspection_result_id  TEXT

type                  TEXT

original_local_path   TEXT
derived_local_path    TEXT NULL
thumbnail_local_path  TEXT NULL

remote_object_key     TEXT NULL

mime_type             TEXT
file_size             INTEGER NULL

width                 INTEGER NULL
height                INTEGER NULL
duration_ms           INTEGER NULL

captured_at           DATETIME

latitude              REAL NULL
longitude             REAL NULL
accuracy_m             REAL NULL

watermark_applied     BOOLEAN

sha256                TEXT

upload_status         TEXT
upload_attempts       INTEGER

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Media types:

PHOTO
VIDEO

Upload states:

LOCAL_ONLY
PENDING
UPLOADING
UPLOADED
FAILED

11. Annotations

11.1 local_annotations

local_id              TEXT PK
server_id             TEXT NULL UNIQUE

media_id              TEXT

annotation_type       TEXT

data_json             TEXT

created_at            DATETIME
updated_at            DATETIME

sync_status           TEXT

Example:

{
  "type": "circle",
  "x": 0.62,
  "y": 0.44,
  "radius": 0.12
}

Coordinates are normalized from 0.0 to 1.0.

Never overwrite the original media.


12. Sync Operations

12.1 local_sync_operations

local_id              TEXT PK

operation_id          TEXT UNIQUE

operation_type        TEXT
entity_type           TEXT
entity_local_id       TEXT

payload_json          TEXT

status                TEXT

attempt_count         INTEGER
last_error            TEXT NULL

created_at            DATETIME
updated_at            DATETIME
next_attempt_at       DATETIME NULL

Operation examples:

CREATE_VEHICLE
CREATE_INSPECTION
CREATE_RESULT
UPDATE_RESULT
CREATE_MEASUREMENT
CREATE_FINDING
CREATE_MEDIA
COMPLETE_INSPECTION

13. Transaction Rules

A user action that changes inspection state must be atomic.

Example: selecting FAIL:

BEGIN TRANSACTION

update inspection_result
create sync_operation

COMMIT

The UI must not show a saved result unless the local transaction succeeded.

For media:

BEGIN TRANSACTION

save media metadata
save local path
create upload operation

COMMIT

The physical file must be durably stored before the database transaction is considered successful.


14. Deletion Rules

Avoid physical deletion of inspection history.

For local drafts:

  • Deletion may be allowed before synchronization.
  • Explicit confirmation required.
  • Never delete a file that belongs to an active inspection without checking references.

For completed/synchronized inspections:

  • Do not delete from the mobile client.
  • Backend controls retention/deletion.

15. Migration Strategy

Every schema change must use a Drift migration.

Example:

v1
  |
  v
v2
  |
  v
v3

Never reset the database in production.

Migration tests are required for every schema version.


16. Local Database Rules

  1. SQLite is the source of truth while offline.
  2. Every user action saves locally first.
  3. UI observes local state.
  4. Sync is asynchronous.
  5. Sync must never block inspection work.
  6. Failed sync must not delete local data.
  7. Completed inspections remain locally readable.
  8. Template versions are immutable.
  9. All IDs are UUID-based.
  10. Media files must have durable local paths.

17. Recommended Drift Structure

lib/core/storage/
  app_database.dart
  tables/
    workshops.dart
    users.dart
    vehicles.dart
    templates.dart
    template_versions.dart
    template_sections.dart
    template_items.dart
    inspections.dart
    inspection_results.dart
    measurements.dart
    findings.dart
    media.dart
    annotations.dart
    sync_operations.dart
  daos/
    vehicle_dao.dart
    template_dao.dart
    inspection_dao.dart
    media_dao.dart
    sync_dao.dart

18. Important Invariants

Inspection

An inspection must reference exactly one template version.

Template

A published template version cannot be mutated.

Result

An inspection result belongs to exactly one inspection and one template item.

Media

Every inspection media object belongs to exactly one inspection and one result/item.

Sync

Every server mutation has an idempotency key.

Completion

A completed inspection cannot be edited.


19. Mobile vs Backend Authority

Mobile is authoritative for:

  • Temporary local state.
  • Offline work.
  • UI responsiveness.
  • Local media capture.

Backend is authoritative for:

  • Tenant permissions.
  • Template publication.
  • Template validation.
  • Inspection completion.
  • Customer visibility.
  • Final report.
  • Audit history.

@pappu687

Copy link
Copy Markdown
Author
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://inspection-platform.local/schemas/inspection-template-version.schema.json",
  "title": "Vehicle Inspection Template Version",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "template",
    "version",
    "inspection_type",
    "sections"
  ],
  "properties": {
    "schema_version": {
      "type": "string",
      "const": "1.0"
    },
    "template": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key",
        "name"
      ],
      "properties": {
        "key": {
          "type": "string",
          "pattern": "^[a-z0-9][a-z0-9._-]*$"
        },
        "name": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "brand": {
          "type": [
            "string",
            "null"
          ]
        },
        "model": {
          "type": [
            "string",
            "null"
          ]
        },
        "model_year_from": {
          "type": [
            "integer",
            "null"
          ]
        },
        "model_year_to": {
          "type": [
            "integer",
            "null"
          ]
        },
        "variant": {
          "type": [
            "string",
            "null"
          ]
        },
        "powertrain": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "version": {
      "type": "integer",
      "minimum": 1
    },
    "inspection_type": {
      "type": "string",
      "minLength": 1
    },
    "settings": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "require_mileage": {
          "type": "boolean"
        },
        "require_vin": {
          "type": "boolean"
        },
        "allow_na": {
          "type": "boolean"
        },
        "completion_requires_all_required_items": {
          "type": "boolean"
        },
        "customer_report_profile": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "sections": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/section"
      }
    }
  },
  "$defs": {
    "condition": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "path",
        "operator",
        "value"
      ],
      "properties": {
        "path": {
          "type": "string"
        },
        "operator": {
          "type": "string",
          "enum": [
            "eq",
            "neq",
            "in",
            "not_in",
            "gt",
            "gte",
            "lt",
            "lte",
            "exists"
          ]
        },
        "value": {}
      }
    },
    "visibility": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "all"
      ],
      "properties": {
        "all": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/condition"
          }
        },
        "any": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/condition"
          }
        }
      }
    },
    "validation": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "minimum": {
          "type": [
            "number",
            "null"
          ]
        },
        "maximum": {
          "type": [
            "number",
            "null"
          ]
        },
        "decimal_places": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0
        },
        "regex": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "evidence": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "photo": {
          "type": "string",
          "enum": [
            "optional",
            "required",
            "required_on_fail"
          ]
        },
        "minimum_photos": {
          "type": "integer",
          "minimum": 0
        },
        "video": {
          "type": "string",
          "enum": [
            "allowed",
            "required",
            "not_allowed"
          ]
        },
        "gps": {
          "type": "string",
          "enum": [
            "optional",
            "required",
            "not_required"
          ]
        },
        "watermark": {
          "type": "boolean"
        }
      }
    },
    "result_config": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "options": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "PASS",
              "FAIL",
              "NA"
            ]
          }
        },
        "derive_from_measurement": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "operator",
            "value"
          ],
          "properties": {
            "operator": {
              "type": "string",
              "enum": [
                "gte",
                "gt",
                "lte",
                "lt",
                "eq"
              ]
            },
            "value": {
              "type": "number"
            }
          }
        }
      }
    },
    "item": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key",
        "title",
        "input_type",
        "required",
        "sort_order"
      ],
      "properties": {
        "key": {
          "type": "string",
          "pattern": "^[a-z0-9][a-z0-9._-]*$"
        },
        "title": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "input_type": {
          "type": "string",
          "enum": [
            "pass_fail",
            "pass_fail_na",
            "measurement",
            "number",
            "text",
            "selection"
          ]
        },
        "required": {
          "type": "boolean"
        },
        "sort_order": {
          "type": "integer",
          "minimum": 0
        },
        "unit": {
          "type": [
            "string",
            "null"
          ]
        },
        "options": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "validation": {
          "$ref": "#/$defs/validation"
        },
        "result_config": {
          "$ref": "#/$defs/result_config"
        },
        "evidence": {
          "$ref": "#/$defs/evidence"
        },
        "visibility": {
          "$ref": "#/$defs/visibility"
        },
        "customer_visible": {
          "type": "boolean"
        },
        "default_severity_on_fail": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "INFO",
            "LOW",
            "MEDIUM",
            "HIGH",
            "CRITICAL",
            null
          ]
        },
        "notes_allowed": {
          "type": "boolean"
        },
        "finding_allowed": {
          "type": "boolean"
        }
      }
    },
    "section": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "key",
        "title",
        "sort_order",
        "items"
      ],
      "properties": {
        "key": {
          "type": "string",
          "pattern": "^[a-z0-9][a-z0-9._-]*$"
        },
        "title": {
          "type": "string",
          "minLength": 1
        },
        "description": {
          "type": [
            "string",
            "null"
          ]
        },
        "sort_order": {
          "type": "integer",
          "minimum": 0
        },
        "visibility": {
          "$ref": "#/$defs/visibility"
        },
        "items": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/item"
          }
        }
      }
    }
  }
}

@pappu687

Copy link
Copy Markdown
Author
openapi: 3.0.3
info:
  title: Vehicle Inspection Mobile API
  version: 1.0.0
  description: |
    Laravel API contract for the Flutter Android inspection client.
    Backend is authoritative for permissions, templates, validation, completion,
    customer visibility and audit.

servers:
  - url: https://api.example.com

security:
  - bearerAuth: []

tags:
  - name: Authentication
  - name: Workshops
  - name: Vehicles
  - name: Templates
  - name: Inspections
  - name: Sync
  - name: Media

paths:

  /api/mobile/login:
    post:
      tags: [Authentication]
      security: []
      summary: Authenticate a mobile user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
      responses:
        '200':
          description: Authenticated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /api/mobile/me:
    get:
      tags: [Authentication]
      summary: Get current user
      responses:
        '200':
          description: Current user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

  /api/mobile/workshops:
    get:
      tags: [Workshops]
      summary: List workshops the user may access
      responses:
        '200':
          description: Workshops
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Workshop'

  /api/mobile/vehicles:
    get:
      tags: [Vehicles]
      summary: Search/list vehicles
      parameters:
        - $ref: '#/components/parameters/WorkshopId'
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
      responses:
        '200':
          description: Vehicle page
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehiclePage'

    post:
      tags: [Vehicles]
      summary: Create vehicle
      parameters:
        - $ref: '#/components/parameters/WorkshopId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VehicleCreateRequest'
      responses:
        '201':
          description: Vehicle created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehicleResponse'
        '422':
          $ref: '#/components/responses/ValidationError'

  /api/mobile/vehicles/{vehicleId}:
    get:
      tags: [Vehicles]
      summary: Get vehicle and inspection history
      parameters:
        - $ref: '#/components/parameters/VehicleId'
      responses:
        '200':
          description: Vehicle
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VehicleDetailResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /api/mobile/templates:
    get:
      tags: [Templates]
      summary: List templates available to the current workshop
      parameters:
        - $ref: '#/components/parameters/WorkshopId'
        - in: query
          name: inspection_type
          schema:
            type: string
        - in: query
          name: vehicle_brand
          schema:
            type: string
        - in: query
          name: vehicle_model
          schema:
            type: string
      responses:
        '200':
          description: Templates
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/TemplateSummary'

  /api/mobile/templates/{templateId}/versions/{version}:
    get:
      tags: [Templates]
      summary: Download an immutable template version
      parameters:
        - $ref: '#/components/parameters/TemplateId'
        - in: path
          name: version
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Template version
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: object
                    additionalProperties: true

  /api/mobile/inspections:
    post:
      tags: [Inspections]
      summary: Create inspection
      parameters:
        - $ref: '#/components/parameters/WorkshopId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InspectionCreateRequest'
      responses:
        '201':
          description: Inspection created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InspectionResponse'
        '422':
          $ref: '#/components/responses/ValidationError'

  /api/mobile/inspections/{inspectionId}:
    get:
      tags: [Inspections]
      summary: Get inspection
      parameters:
        - $ref: '#/components/parameters/InspectionId'
      responses:
        '200':
          description: Inspection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InspectionResponse'

  /api/mobile/inspections/{inspectionId}/complete:
    post:
      tags: [Inspections]
      summary: Complete inspection
      parameters:
        - $ref: '#/components/parameters/InspectionId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InspectionCompleteRequest'
      responses:
        '200':
          description: Completed inspection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InspectionResponse'
        '409':
          description: Completion rejected because required data/evidence is missing
        '422':
          $ref: '#/components/responses/ValidationError'

  /api/mobile/media/presign:
    post:
      tags: [Media]
      summary: Request a pre-signed object-storage upload
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MediaPresignRequest'
      responses:
        '200':
          description: Upload instructions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaPresignResponse'

  /api/mobile/media/complete:
    post:
      tags: [Media]
      summary: Confirm media upload
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MediaCompleteRequest'
      responses:
        '200':
          description: Media registered
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaResponse'

  /api/mobile/sync:
    post:
      tags: [Sync]
      summary: Submit idempotent offline mutations
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncRequest'
      responses:
        '200':
          description: Sync results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncResponse'
        '207':
          description: Multi-status; some operations succeeded and others failed

components:

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Sanctum

  parameters:
    WorkshopId:
      in: query
      name: workshop_id
      required: true
      schema:
        type: string
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      required: true
      schema:
        type: string
        format: uuid
    VehicleId:
      in: path
      name: vehicleId
      required: true
      schema:
        type: string
    TemplateId:
      in: path
      name: templateId
      required: true
      schema:
        type: string
    InspectionId:
      in: path
      name: inspectionId
      required: true
      schema:
        type: string

  responses:
    Unauthorized:
      description: Authentication required
    NotFound:
      description: Resource not found
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

  schemas:

    LoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
          format: email
        password:
          type: string
          format: password

    LoginResponse:
      type: object
      required: [token, user]
      properties:
        token:
          type: string
        user:
          $ref: '#/components/schemas/User'

    User:
      type: object
      required: [id, name, email, role]
      properties:
        id:
          type: string
        name:
          type: string
        email:
          type: string
        role:
          type: string

    Workshop:
      type: object
      required: [id, name]
      properties:
        id:
          type: string
        name:
          type: string
        code:
          type: string
          nullable: true
        status:
          type: string

    VehicleCreateRequest:
      type: object
      required: [brand, model]
      properties:
        local_id:
          type: string
          format: uuid
        brand:
          type: string
        model:
          type: string
        year:
          type: integer
          nullable: true
        variant:
          type: string
          nullable: true
        fuel_type:
          type: string
          nullable: true
        vin:
          type: string
          nullable: true
        registration_number:
          type: string
          nullable: true
        number_plate:
          type: string
          nullable: true
        owner_name:
          type: string
          nullable: true
        owner_phone:
          type: string
          nullable: true

    Vehicle:
      allOf:
        - $ref: '#/components/schemas/VehicleCreateRequest'
        - type: object
          required: [id, workshop_id]
          properties:
            id:
              type: string
            workshop_id:
              type: string
            status:
              type: string

    VehiclePage:
      type: object
      required: [data, meta]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Vehicle'
        meta:
          type: object
          additionalProperties: true

    VehicleResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: '#/components/schemas/Vehicle'

    VehicleDetailResponse:
      type: object
      required: [data, inspections]
      properties:
        data:
          $ref: '#/components/schemas/Vehicle'
        inspections:
          type: array
          items:
            $ref: '#/components/schemas/InspectionSummary'

    TemplateSummary:
      type: object
      required: [id, name, inspection_type, latest_version]
      properties:
        id:
          type: string
        name:
          type: string
        inspection_type:
          type: string
        latest_version:
          type: integer
        brand:
          type: string
          nullable: true
        model:
          type: string
          nullable: true

    InspectionCreateRequest:
      type: object
      required:
        - local_id
        - vehicle_id
        - template_version_id
        - inspection_type
      properties:
        local_id:
          type: string
          format: uuid
        vehicle_id:
          type: string
        template_version_id:
          type: string
        inspection_type:
          type: string
        mileage:
          type: integer
          nullable: true
        internal_reference:
          type: string
          nullable: true
        started_at:
          type: string
          format: date-time

    InspectionCompleteRequest:
      type: object
      required: [local_id]
      properties:
        local_id:
          type: string
          format: uuid

    InspectionSummary:
      type: object
      required: [id, inspection_type, status, started_at]
      properties:
        id:
          type: string
        inspection_type:
          type: string
        status:
          type: string
        overall_result:
          type: string
          nullable: true
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true

    InspectionResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          additionalProperties: true

    MediaPresignRequest:
      type: object
      required: [local_id, inspection_id, inspection_result_id, mime_type, sha256]
      properties:
        local_id:
          type: string
          format: uuid
        inspection_id:
          type: string
        inspection_result_id:
          type: string
        mime_type:
          type: string
        file_size:
          type: integer
        sha256:
          type: string
        filename:
          type: string

    MediaPresignResponse:
      type: object
      required: [upload_url, object_key]
      properties:
        upload_url:
          type: string
          format: uri
        object_key:
          type: string
        expires_at:
          type: string
          format: date-time

    MediaCompleteRequest:
      type: object
      required: [local_id, object_key]
      properties:
        local_id:
          type: string
          format: uuid
        object_key:
          type: string
        width:
          type: integer
          nullable: true
        height:
          type: integer
          nullable: true
        duration_ms:
          type: integer
          nullable: true
        captured_at:
          type: string
          format: date-time
        latitude:
          type: number
          nullable: true
        longitude:
          type: number
          nullable: true
        sha256:
          type: string

    MediaResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          additionalProperties: true

    SyncRequest:
      type: object
      required: [operations]
      properties:
        operations:
          type: array
          items:
            $ref: '#/components/schemas/SyncOperation'

    SyncOperation:
      type: object
      required:
        - operation_id
        - operation_type
        - entity_type
        - entity_local_id
        - payload
      properties:
        operation_id:
          type: string
          format: uuid
        operation_type:
          type: string
        entity_type:
          type: string
        entity_local_id:
          type: string
          format: uuid
        payload:
          type: object
          additionalProperties: true

    SyncResponse:
      type: object
      required: [results]
      properties:
        results:
          type: array
          items:
            type: object
            required: [operation_id, status]
            properties:
              operation_id:
                type: string
              status:
                type: string
                enum: [SYNCED, FAILED, CONFLICT]
              server_id:
                type: string
                nullable: true
              error_code:
                type: string
                nullable: true
              error_message:
                type: string
                nullable: true

    ErrorResponse:
      type: object
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string

@pappu687

Copy link
Copy Markdown
Author

MOBILE_VERTICAL_SLICES.md

Vehicle Inspection Platform — Mobile Vertical Slices

Purpose: Exact implementation roadmap for Codex / Antigravity / similar AI coding editors.

Execution model: Inline execution. No sub-agents.


0. Rules for the AI Coding Editor

For every slice:

  1. Read the current repository before editing.
  2. Implement only the requested slice.
  3. Do not invent product behavior where the specification is silent.
  4. Do not hard-code Toyota-specific item IDs.
  5. Do not hard-code the 160-point checklist into Flutter.
  6. Keep template behavior backend-driven.
  7. Use repository/application/domain boundaries.
  8. Write tests with the implementation.
  9. Run formatter.
  10. Run static analysis.
  11. Run relevant tests.
  12. Do not break earlier slices.
  13. Keep the app runnable after every slice.
  14. Do not delete unsynchronized local data.
  15. Do not mutate completed inspections.
  16. Update documentation when behavior changes.
  17. If a dependency is required, state why before adding it.
  18. Prefer small, focused changes.
  19. Do not implement future slices opportunistically.
  20. At the end, report files changed, tests run, and any unresolved issue.

1. Slice 0 — Repository Bootstrap

Goal

Create the Flutter Android project foundation.

Implement

  • Flutter Android project.
  • Environment configuration.
  • Routing.
  • Dependency injection.
  • Base error model.
  • Connectivity abstraction.
  • Secure storage abstraction.
  • Drift database skeleton.
  • Feature-first folder structure.
  • Test infrastructure.

Do not implement

  • Real API.
  • Camera.
  • Sync.
  • Full template renderer.
  • PDF.
  • Video.

Suggested structure

lib/
  core/
    error/
    network/
    storage/
    permissions/
    connectivity/
    security/
    routing/

  features/
    authentication/
    vehicles/
    templates/
    inspections/
    media/
    sync/
    reports/
    settings/

Acceptance criteria

  • App launches on Android.
  • Navigation works.
  • Database can initialize.
  • Tests run.
  • Analyzer is clean.

2. Slice 1 — Authentication + Local User

Goal

Allow a mechanic to authenticate and retain session state securely.

Implement

  • Login screen.
  • Authentication repository.
  • Secure token storage.
  • User model.
  • Current-user state.
  • Logout.
  • Authentication guard.

Acceptance criteria

  • Valid credentials enter Home.
  • Invalid credentials show a useful error.
  • Restarting app preserves session when valid.
  • Token is not stored in plain preferences.
  • Logout removes session credentials.
  • Local unsynchronized inspection data is not silently deleted on logout.

3. Slice 2 — Vehicle Registry

Goal

Create and browse vehicles locally.

Implement

  • Vehicle list.
  • Search.
  • Create vehicle.
  • Vehicle details.
  • Local Drift vehicle table.
  • Repository.
  • Validation.

Fields

Brand
Model
Year
Variant
Fuel type
VIN
Registration number
Number plate
Owner name
Owner phone

Acceptance criteria

  • Create vehicle offline.
  • Search offline.
  • Close/reopen app.
  • Vehicle remains available.
  • No network required.

4. Slice 3 — Template Schema + Local Renderer

Goal

Render inspection templates dynamically.

Implement

  • Template Dart domain models.
  • JSON parsing.
  • JSON schema validation in development/tests.
  • Drift template storage.
  • Section renderer.
  • Item renderer.

Supported item types

pass_fail
pass_fail_na
measurement
number
text
selection

Critical rule

Do not use:

if (item.key == "front_pad_thickness")

Use:

switch (item.inputType)

Acceptance criteria

  • A test JSON template can render without hard-coded item IDs.
  • Sections render in sort order.
  • Items render in sort order.
  • Unknown item types produce a controlled error.
  • Template version is stored locally.

5. Slice 4 — Inspection Creation

Goal

Create an inspection for a vehicle using a frozen template version.

Implement

  • Start inspection flow.
  • Inspection setup screen.
  • Inspection local table.
  • Inspection repository.
  • Template version association.
  • Mileage.
  • Inspection type.
  • Inspector.

Acceptance criteria

  • User selects vehicle.
  • User selects inspection type/template.
  • Inspection is created locally.
  • Template version is stored.
  • Template version cannot change after start.

6. Slice 5 — Checklist UX

Goal

Allow the mechanic to complete multiple sections/items.

Implement

  • Inspection overview.
  • Section list.
  • Section screen.
  • Item screen.
  • Previous/next navigation.
  • Progress.
  • Local autosave.

UX requirements

Show:

  • Section progress.
  • Overall progress.
  • Failed count.
  • Required-item state.
  • Unsaved/sync state.

Acceptance criteria

  • At least 3 sections.
  • At least 10 test items.
  • User can navigate forward/back.
  • Every result persists immediately.
  • App restart restores exact inspection state.

7. Slice 6 — Results + Measurements

Goal

Implement backend-defined result behavior.

Implement

  • PASS.
  • FAIL.
  • N/A.
  • Text.
  • Number.
  • Measurement.
  • Min/max validation.
  • Derived pass/fail.

Acceptance criteria

  • PASS/FAIL persists locally.
  • N/A only appears when configured.
  • Measurement validation is template-driven.
  • Derived result updates immediately.
  • Invalid measurement cannot be treated as valid.

8. Slice 7 — Findings + Notes

Goal

Allow actionable failures.

Implement

  • Item notes.
  • Finding creation.
  • Finding severity.
  • Description.
  • Recommendation.
  • Finding persistence.

Acceptance criteria

  • FAIL can create finding when allowed.
  • Finding can be edited before completion.
  • Finding survives restart.
  • Severity is template/backend configurable.

9. Slice 8 — Camera + Gallery

Goal

Capture photo evidence against a specific inspection item.

Implement

  • Camera.
  • Gallery.
  • Multiple selection.
  • Local file storage.
  • Thumbnail generation.
  • Media metadata.
  • Inspection-result association.

Acceptance criteria

  • Multiple photos can be attached to one item.
  • Photos survive app restart.
  • Original image is retained.
  • Thumbnail displays immediately.
  • Upload is not required to continue.

10. Slice 9 — Watermark + Metadata

Goal

Add inspection identity to evidence.

Implement

  • Capture timestamp.
  • VIN metadata.
  • Inspection identity.
  • Optional GPS.
  • Watermarked derivative.
  • SHA-256.

Acceptance criteria

  • Original is preserved.
  • Derived/watermarked copy exists.
  • Timestamp is stored.
  • GPS can be absent without crashing.
  • SHA-256 is deterministic.

11. Slice 10 — Annotation

Goal

Annotate evidence.

Implement

  • Image editor screen.
  • Circle.
  • Arrow.
  • Undo.
  • Clear.
  • Save.
  • Normalized coordinates.

Acceptance criteria

  • Original image remains unchanged.
  • Annotation survives restart.
  • Annotation is stored as data.
  • Resizing image does not misplace annotation.

12. Slice 11 — Video

Goal

Capture video evidence.

Implement

  • Video capture.
  • Local storage.
  • Thumbnail.
  • Duration.
  • Item association.
  • Upload-ready metadata.

Acceptance criteria

  • Video remains available offline.
  • App restart does not lose video.
  • Large files do not block the UI.

13. Slice 12 — Offline Sync Engine

Goal

Synchronize local mutations with Laravel.

Implement

  • Connectivity detection.
  • Sync queue.
  • Operation UUID.
  • Idempotency key.
  • Retry.
  • Backoff.
  • Server result handling.
  • Failed operation UI.

Required operation types

CREATE_VEHICLE
CREATE_INSPECTION
CREATE_RESULT
UPDATE_RESULT
CREATE_MEASUREMENT
CREATE_FINDING
CREATE_MEDIA
COMPLETE_INSPECTION

Acceptance test

1. Disable network.
2. Create vehicle.
3. Start inspection.
4. Complete several items.
5. Add findings.
6. Capture photos.
7. Complete inspection.
8. Kill app.
9. Restart app.
10. Verify all local data exists.
11. Restore network.
12. Sync.
13. Verify backend contains exactly one vehicle and one inspection.
14. Verify media exists.

14. Slice 13 — Real API Integration

Goal

Replace development fixtures with Laravel API.

Implement

  • OpenAPI models/mappers.
  • Authentication API.
  • Vehicle API.
  • Template API.
  • Inspection API.
  • Sync API.
  • Media pre-signing API.

Acceptance criteria

  • No hard-coded fixture data in production paths.
  • API failures preserve local state.
  • Server IDs are stored.
  • Idempotency keys are sent.
  • Unauthorized responses are handled without data loss.

15. Slice 14 — Template Version Updates

Goal

Support backend-published template versions safely.

Implement

  • Template list API.
  • Version download.
  • Local checksum.
  • Template cache.
  • Version selection.

Critical acceptance test

Download v1.
Start Inspection A.

Backend publishes v2.

Download v2.

Inspection A still uses v1.

Start Inspection B.

Inspection B uses v2.

16. Slice 15 — Completion Validation

Goal

Prevent invalid inspections from being completed.

Implement

  • Required item validation.
  • Required evidence validation.
  • Measurement validation.
  • Conditional item validation.
  • Local completion.
  • Server completion.

Acceptance criteria

Example:

FAIL
+
photo required
+
0 photos
=
cannot complete

Offline:

valid local inspection
=
mark completed locally
+
queue completion

Server repeats validation.


17. Slice 16 — Vehicle History

Goal

Show repeated inspection history.

Implement

  • Vehicle history API.
  • Local history.
  • Inspection list.
  • Inspection detail.
  • Inspector identity.
  • Date.
  • Result.
  • Reinspection/superseding relationship.

Acceptance criteria

A vehicle can display:

Inspection #1 — John — Jan
Inspection #2 — Sarah — Feb
Inspection #3 — John — Mar

No inspection overwrites another.


18. Slice 17 — Recovery & Hardening

Goal

Make the app safe under real workshop conditions.

Implement

  • Database migration testing.
  • App restart recovery.
  • Storage-low warnings.
  • Authentication expiry recovery.
  • Sync diagnostics.
  • Permission recovery.
  • Retry UX.
  • Error boundaries.
  • Safe file cleanup.

Acceptance criteria

Test:

  • App killed during inspection.
  • App killed during media processing.
  • App killed during sync.
  • Network disappears during upload.
  • Token expires during sync.
  • Device restarts.
  • Permission denied.
  • S3 upload fails.
  • Backend returns validation error.

No inspection data may be silently lost.


19. Slice 18 — Full Toyota-Style Template

Only after the generic engine is stable.

Implement

  • Import/build the Toyota-style base template.
  • All sections.
  • Configurable outcomes.
  • Additional inspection items.
  • Hybrid-specific sections.
  • Evidence requirements where business rules require them.

Do not encode the Toyota checklist into Flutter code.


20. Slice Completion Checklist

For every slice:

[ ] Implementation complete
[ ] Unit tests
[ ] Repository tests where applicable
[ ] Widget tests where applicable
[ ] Integration test where applicable
[ ] Analyzer clean
[ ] Formatter clean
[ ] Existing tests pass
[ ] Offline behavior tested where applicable
[ ] Loading state
[ ] Empty state
[ ] Error state
[ ] Retry state
[ ] No data-loss path
[ ] Documentation updated

21. AI Editor Prompt Template

Use this structure when starting each slice:

You are implementing Slice N of the Vehicle Inspection Platform.

Read:
- MOBILE_APP_TECHNICAL_SPEC.md
- MOBILE_DATA_MODEL.md
- TEMPLATE_SCHEMA.json
- MOBILE_API.yaml
- MOBILE_VERTICAL_SLICES.md

Implement ONLY Slice N.

Do not:
- implement future slices
- invent business rules
- hard-code template item IDs
- hard-code Toyota-specific behavior
- bypass repository/domain boundaries
- delete unsynchronized data

Before editing:
1. Inspect the repository.
2. Identify existing architecture.
3. Identify tests.
4. State the files you expect to change.

During implementation:
1. Keep changes focused.
2. Add tests.
3. Preserve offline-first behavior.
4. Use existing patterns unless they conflict with the specification.

After implementation:
1. Run formatter.
2. Run analyzer.
3. Run relevant tests.
4. Report files changed.
5. Report commands executed.
6. Report test results.
7. Report unresolved issues.

22. Recommended Implementation Order

Slice 0  Bootstrap
   ↓
Slice 1  Authentication
   ↓
Slice 2  Vehicles
   ↓
Slice 3  Template renderer
   ↓
Slice 4  Inspection creation
   ↓
Slice 5  Checklist UX
   ↓
Slice 6  Results / measurements
   ↓
Slice 7  Findings
   ↓
Slice 8  Photos
   ↓
Slice 9  Metadata / watermark
   ↓
Slice 10 Annotation
   ↓
Slice 11 Video
   ↓
Slice 12 Offline sync
   ↓
Slice 13 Real API
   ↓
Slice 14 Template versions
   ↓
Slice 15 Completion
   ↓
Slice 16 Vehicle history
   ↓
Slice 17 Hardening
   ↓
Slice 18 Toyota template

23. Critical Milestones

Milestone A — Offline Checklist

The mechanic can:

Create vehicle
→ start inspection
→ complete checklist
→ close app
→ reopen
→ continue

Milestone B — Evidence

The mechanic can:

FAIL item
→ take photos
→ annotate
→ save
→ continue offline

Milestone C — Synchronization

The mechanic can:

Complete offline
→ reconnect
→ synchronize
→ backend has exactly one inspection

Milestone D — Production Template

The mechanic can:

Backend publishes template
→ mobile downloads version
→ inspection uses frozen version

Milestone E — Production Inspection

The full inspection can:

Run offline
+
Capture evidence
+
Validate
+
Complete
+
Synchronize
+
Appear in vehicle history

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment