Files

26 KiB

Project Template: Native Betting Study App

This is the canonical working plan and progress log for the project. Use this file for progress notes, next steps, and status updates.

Current Status

Done So Far

  • Android was renamed from the old study app into com.hermes.app, with the app entry, theme, repository, media, and feature screens rewritten around the backend-backed Hermes flow.
  • Android no longer relies on the sample fixture; it boots from backend session and round data, uses /health.server_time for clock sync, buffers analytics, and flushes them to the backend.
  • Android localization is in place for English and Swedish, including the remaining locale toggle labels and session language display.
  • Backend now exposes server_time from /health, publishes the matching OpenAPI contract, and records audit events for session, bet, event, market, odds, and settlement actions.
  • Backend smoke coverage was added for server_time and audit logging.
  • iOS now follows the backend-backed flow, syncs clock from /health, flushes analytics, and uses localized language labels instead of hardcoded EN and SV.
  • iOS source was updated for the backend-backed session and round flow, including the real preview cue points and localized session strings.
  • iOS settings now uses localized copy instead of a scaffold placeholder, and the screen is wired into the root scroll flow.
  • Android debug build passes with ./gradlew :app:assembleDebug.
  • Backend tests pass with cargo test.
  • Backend localization bundles now have a contract test, and the localization catalog matches the shipped English and Swedish keys.
  • The research export path is documented under docs/research/export-path.md.
  • Backend bet coverage now includes a direct regression for late-lock rejection.
  • Backend bet coverage now includes invalid-session and invalid-market rejection regressions, plus settlement override coverage.
  • Backend analytics coverage now includes a representative key user flow batch.
  • iOS project scaffolding now exists as mobile/ios-app/HermesApp.xcodeproj with a shared scheme and XCTest target.
  • iOS sources and tests build and pass in Xcode on an iPhone simulator, and a standalone localization/error-mapping harness passes.
  • Android local unit tests now cover localization, error mapping, and analytics batch conversion, and ./gradlew testDebugUnitTest passes with the Android SDK installed.
  • Android swipe-down gesture handling now uses cumulative drag distance, and edge-case tests cover upward drags, slow swipes, and per-gesture single-trigger behavior.
  • iOS now defaults to a demo test mode with mock rounds, and the primary round flow is a fullscreen swipe-driven video experience with a 15-second lock timer and accelerated reveal transition.
  • The fullscreen round interaction contract is now documented in docs/mobile-fullscreen-round-flow.md, including demo-mode rules, side-tint swipe feedback, and the requirement to finish the accelerated round clip before reveal starts.

Still Open

  • Continue through the remaining plan phases and finish any leftover localization and polish work.
  • Keep the iOS project in sync with the plan as the app grows.
  • Keep expanding tests around session, odds, settlement, and analytics behavior.
  • Align Android with the clarified fullscreen swipe flow and demo-mode testing path.

1. Purpose

Build a native mobile prototype for iOS and Android for a research study. Users watch a short sports clip before a critical moment, choose an outcome before a lock time, and then watch the reveal segment.

The app must feel premium, fast, clear, mobile-first, and highly polished. It must feel as easy and fun to use as possible as it's needed for the research study. As mentioned, it is for research and prototype use, not first-release real-money gambling. However, the app will be tests on a small group and compared against traditional betting sites. We thereful need to make this as premium and good as possible to make sure that we can make meaningful observations and discussionpoints from the outcome.

The app must support:

  • English
  • Swedish

Every user-facing string must be localizable from day one.


2. Product Goals

Primary goals

  • Native feel on both iOS and Android
  • Very high responsiveness
  • Smooth video playback
  • Strong gesture support
  • Clear and fair lock timing
  • Excellent readability and touch ergonomics
  • Premium visual quality
  • Stable architecture for long-term iteration

Secondary goals

  • Low initial app size
  • Reusable backend
  • Easy experimentation between:
    • control
    • modern

Non-goals

  • Real money betting in the first version
  • Full regulatory launch in version one

3. Technology Stack

Backend

  • Rust

  • Axum

  • Tokio

  • SQLx

  • PostgreSQL

  • Valkey (redis)

  • tracing

  • thiserror + anyhow

  • validator

  • OpenTelemetry

  • Docker

  • docker compose

iOS

  • Swift
  • SwiftUI
  • UIKit where needed
  • AVFoundation / AVPlayer
  • URLSession
  • async/await
  • native haptics
  • native gesture handling

Android

  • Kotlin
  • Jetpack Compose
  • Media3 / ExoPlayer
  • Kotlin coroutines
  • Kotlin Flow
  • Kotlinx Serialization
  • kotlinx-datetime
  • kotlinx-collections-immutable
  • native haptics
  • native gesture handling

Shared principles

  • No cross-platform UI
  • Native UI on each platform
  • Shared contracts through generated schemas and strict API specs
  • Prefer Kotlinx libraries broadly inside Android and Kotlin tooling
  • If a small shared Kotlin module is added later, prefer:
    • coroutines
    • Flow
    • Kotlinx Serialization
    • kotlinx-datetime
    • kotlinx-collections-immutable

4. Architecture Overview

The system has three layers:

4.1 Client layer

Two separate native apps:

  • ios-app
  • android-app

Client responsibilities:

  • UI rendering
  • video playback
  • gestures
  • odds overlay
  • local state
  • media prefetch
  • clock sync
  • sending user selections
  • analytics and telemetry capture
  • localization in English and Swedish

4.2 API layer

Rust backend responsible for:

  • auth
  • sessions
  • event feed
  • odds distribution
  • bet acceptance
  • settlement
  • experiment assignment
  • analytics ingestion
  • audit trail

4.3 Data layer

  • PostgreSQL for durable relational data
  • Redis for short-lived state and caching
  • object storage / CDN for HLS assets and media metadata

5. Repository Structure

root/
  README.md
  docs/
    product/
    architecture/
    api/
    ux/
    research/
    localization/
    analytics/
  backend/
    Cargo.toml
    Cargo.lock
    migrations/
    src/
      main.rs
      config.rs
      app_state.rs
      error.rs
      telemetry.rs
      db/
      auth/
      users/
      sessions/
      events/
      media/
      markets/
      outcomes/
      odds/
      bets/
      settlement/
      analytics/
      experiments/
      localization/
      admin/
      jobs/
      utils/
    tests/
  mobile/
    ios-app/
    android-app/
  contracts/
    openapi/
    protobuf/
    localization/
  fixtures/
    videos/
    manifests/
    test-data/
  scripts/
  infra/

6. Core Principles

  1. The server is authoritative

    • lock time is determined by the server
    • odds acceptance is determined by the server
    • settlement is determined by the server
  2. The client must not decide fairness

    • the client sends intent
    • the server accepts or rejects
  3. Video must never block on UI updates

    • player and overlay must be architecturally separate
  4. Explicit state machines are required

    • client and server must follow the same state model
  5. Optimize for smoothness

    • low input latency
    • consistent animation timing
    • clear hierarchy
    • readable odds and timers
  6. Localization is first-class

    • all UI strings in English and Swedish
    • no hardcoded copy in views
    • use stable translation keys
  7. Log aggressively

    • log as many meaningful user actions as possible
    • support later UX optimization and research analysis
    • keep logs structured and queryable

7. Main Product Flow

A user session contains multiple rounds.

Each round:

  1. Prefetch the next event
  2. Show the preview segment
  3. Update odds during preview
  4. User selects an outcome before lock_at
  5. Show locked state
  6. Play reveal segment
  7. Show result
  8. Prepare the next round

8. Domain Model

Core entities

User

  • id
  • external_ref
  • created_at
  • preferred_language
  • device_platform

Session

  • id
  • user_id
  • started_at
  • ended_at
  • experiment_variant
  • app_version
  • device_model
  • os_version
  • locale_code

Event

  • id
  • sport_type
  • source_ref
  • title_en
  • title_sv
  • status
  • preview_start_ms
  • preview_end_ms
  • reveal_start_ms
  • reveal_end_ms
  • lock_at
  • settle_at

EventMedia

  • id
  • event_id
  • media_type
  • hls_master_url
  • poster_url
  • duration_ms
  • preview_start_ms
  • preview_end_ms
  • reveal_start_ms
  • reveal_end_ms

Market

  • id
  • event_id
  • question_key
  • market_type
  • status
  • lock_at
  • settlement_rule_key

Outcome

  • id
  • market_id
  • outcome_code
  • label_key
  • sort_order

OddsVersion

  • id
  • market_id
  • version_no
  • created_at
  • is_current

OutcomeOdds

  • id
  • odds_version_id
  • outcome_id
  • decimal_odds
  • fractional_num
  • fractional_den

BetIntent

  • id
  • user_id
  • session_id
  • event_id
  • market_id
  • outcome_id
  • idempotency_key
  • client_sent_at
  • server_received_at
  • accepted
  • acceptance_code
  • accepted_odds_version_id

Settlement

  • id
  • market_id
  • settled_at
  • winning_outcome_id

ExperimentAssignment

  • id
  • user_id
  • session_id
  • variant
  • assigned_at

LocalizationKey

  • id
  • key_name
  • description

LocalizationValue

  • id
  • localization_key_id
  • locale_code
  • text_value

AnalyticsEventType

  • id
  • event_name
  • description

AnalyticsEvent

  • id
  • session_id
  • user_id
  • analytics_event_type_id
  • occurred_at
  • app_screen_id
  • event_sequence_no

AnalyticsEventAttribute

  • id
  • analytics_event_id
  • attribute_name
  • attribute_value

AuditLog

  • id
  • actor_type
  • actor_id
  • action_name
  • created_at
  • target_type
  • target_id

AuditLogAttribute

  • id
  • audit_log_id
  • attribute_name
  • attribute_value

9. State Machines

Event lifecycle

  • scheduled
  • prefetch_ready
  • preview_open
  • locking
  • locked
  • reveal_open
  • settled
  • archived

Client round state

  • idle
  • prefetching
  • ready
  • preview_playing
  • selection_pending
  • selection_submitting
  • selection_accepted
  • locked
  • reveal_playing
  • result_visible
  • transitioning
  • error

Bet acceptance state

  • received
  • validated
  • accepted
  • rejected_too_late
  • rejected_invalid_market
  • rejected_invalid_session
  • rejected_duplicate
  • settled

10. Backend Modules

10.1 auth

Responsibilities:

  • anonymous study sign-in or simple participant sign-in
  • token issuance
  • session binding

10.2 users

Responsibilities:

  • user creation
  • locale preference
  • participant metadata

10.3 sessions

Responsibilities:

  • start and end sessions
  • attach device metadata
  • track experiment group

10.4 events

Responsibilities:

  • list events
  • return next round
  • return event manifest

10.5 media

Responsibilities:

  • expose media metadata
  • expose HLS URLs
  • expose cue points

10.6 markets

Responsibilities:

  • create and query markets
  • attach outcomes
  • manage lock time

10.7 odds

Responsibilities:

  • publish odds versions
  • stream odds changes
  • store full relational odds history

10.8 bets

Responsibilities:

  • receive bet intents
  • validate requests
  • accept or reject against lock time
  • enforce idempotency

10.9 settlement

Responsibilities:

  • store winning outcome
  • expose settlement result
  • audit all settlement actions

10.10 experiments

Responsibilities:

  • assign control or modern
  • return feature flags
  • keep assignment stable per session

10.11 analytics

Responsibilities:

  • ingest all telemetry
  • normalize event types
  • write event attributes to child tables
  • support analysis exports

10.12 localization

Responsibilities:

  • serve language bundles
  • support English and Swedish
  • version translation payloads

10.13 admin

Responsibilities:

  • create events
  • create markets
  • create odds versions
  • publish fixtures
  • set settlements

11. Database Design

Use SQLx migrations only. Do not use JSONB. Use normalized relational tables instead.

Required tables

  • users
  • sessions
  • events
  • event_media
  • markets
  • outcomes
  • odds_versions
  • outcome_odds
  • bet_intents
  • settlements
  • experiment_assignments
  • localization_keys
  • localization_values
  • analytics_event_types
  • analytics_events
  • analytics_event_attributes
  • audit_logs
  • audit_log_attributes

General rules

  • UUID primary keys
  • timestamptz for all timestamps
  • no JSONB columns
  • use child tables for attributes and event payloads
  • store translation text in relational tables
  • use foreign keys everywhere possible

Key indexes

  • sessions.user_id
  • sessions.started_at
  • events.status
  • markets.event_id
  • markets.lock_at
  • bet_intents.session_id
  • bet_intents.market_id
  • bet_intents.idempotency_key
  • analytics_events.session_id
  • analytics_events.user_id
  • analytics_events.analytics_event_type_id
  • audit_logs.created_at

Notes

For analytics and audit data, avoid schemaless storage. Use parent-child tables such as:

  • analytics_events
  • analytics_event_attributes
  • audit_logs
  • audit_log_attributes

This allows queryable, indexable telemetry without JSONB.


12. API Design

12.1 Style

  • REST for initialization and main flows
  • WebSocket or SSE for live odds and state updates
  • JSON transport is acceptable over the API
  • database storage must remain relational
  • version prefix: /api/v1

12.2 Core endpoints

Session

  • POST /api/v1/session/start
  • POST /api/v1/session/end
  • GET /api/v1/session/me

Feed and events

  • GET /api/v1/feed/next
  • GET /api/v1/events/{event_id}
  • GET /api/v1/events/{event_id}/manifest

Markets and odds

  • GET /api/v1/events/{event_id}/markets
  • GET /api/v1/markets/{market_id}/odds/current
  • GET /api/v1/stream

Bets

  • POST /api/v1/bets/intent
  • GET /api/v1/bets/{bet_intent_id}

Results

  • GET /api/v1/events/{event_id}/result

Analytics

  • POST /api/v1/analytics/batch

Experiments

  • GET /api/v1/experiments/config

Localization

  • GET /api/v1/localization/{locale_code}

Admin

  • POST /api/v1/admin/events
  • POST /api/v1/admin/markets
  • POST /api/v1/admin/odds
  • POST /api/v1/admin/settlements

13. Native Client Architecture

13.1 Common structure

Each app must have:

  • Presentation
  • Feature state
  • Domain
  • Networking
  • Media
  • Localization
  • Analytics
  • Persistence

13.2 iOS structure

ios-app/
  App/
  Core/
    Networking/
    DesignSystem/
    Analytics/
    Media/
    Haptics/
    Gestures/
    Localization/
  Features/
    Onboarding/
    Feed/
    Round/
    Selection/
    Reveal/
    Result/
    Session/
    Settings/
  Resources/
  Tests/

iOS rules

  • SwiftUI by default
  • UIKit only where precision control is needed
  • AVPlayer for playback
  • separate player state from overlay state
  • all user strings loaded from localization layer
  • English and Swedish supported at launch

13.3 Android structure

android-app/
  app/
  core/
    network/
    designsystem/
    analytics/
    media/
    haptics/
    gestures/
    localization/
  data/
  domain/
  feature/
    onboarding/
    feed/
    round/
    selection/
    reveal/
    result/
    session/
    settings/
  benchmark/
  androidTest/

Android rules

  • Jetpack Compose by default
  • Media3 / ExoPlayer for playback
  • ViewModel + immutable state
  • Kotlin coroutines and Flow everywhere practical
  • Kotlinx Serialization for API models
  • kotlinx-datetime for time handling
  • kotlinx-collections-immutable for UI collections
  • optimize recomposition carefully
  • English and Swedish supported at launch

14. Gesture Specification

The app must feel mobile-native and gesture-friendly.

Supported gestures

  • tap to select outcome
  • vertical swipe between rounds or cards where appropriate
  • swipe down to dismiss modal
  • edge gestures only where platform expectations exist
  • no horizontal scrubbing during an active betting round

Rules

  • gestures must never make selection state ambiguous
  • a critical choice must get clear visual confirmation
  • avoid complex multi-touch gestures
  • no gesture may fight system navigation
  • touch targets must be large and comfortable

Haptics

  • subtle feedback when a selection is accepted
  • subtle feedback when the market locks
  • never overuse haptics

15. Screens

Onboarding

  • study intro
  • consent
  • language selection
  • start session

Feed

  • next round preview card
  • compact event information
  • clear call to action

Active round

  • video
  • countdown
  • odds panel
  • selection controls
  • lock status
  • pause or exit control

Locked state

  • selected outcome highlighted
  • lock status clearly visible
  • odds frozen for the user

Reveal

  • reveal segment
  • simple visual emphasis
  • no chaotic effects

Result

  • what happened
  • user selection
  • outcome display
  • next round entry

Pause and exit

  • easy to reach
  • easy to confirm
  • no manipulative friction

16. Design System

Visual direction

  • premium dark base
  • strong contrast
  • limited accent colors
  • clean typography
  • large video surface
  • restrained motion
  • high readability

Design tokens

Define centrally:

  • colors
  • spacing
  • radii
  • elevation
  • typography scale
  • icon sizes
  • motion durations
  • motion curves

Motion rules

  • quick feedback on touch
  • calm transitions
  • one consistent motion language
  • no flashing or overly aggressive effects
  • reveal should feel polished, not loud

17. Localization Requirements

The app must ship with:

  • English
  • Swedish

Rules

  • every visible string uses a localization key
  • no hardcoded strings in views
  • backend can serve localization bundles if needed
  • use pluralization support where relevant
  • event and market text must support both languages
  • analytics must include active locale for each session

Suggested locale codes

  • en
  • sv

18. Video and Media Pipeline

Requirements

  • HLS as the default streaming format
  • short segments
  • correct keyframes around:
    • preview start
    • preview end
    • lock cue
    • reveal start
    • reveal end

Playback goals

  • prefetch the next clip early
  • keep player and overlay independent
  • recover gracefully from playback errors
  • support poster fallback

Cue points

Each media item must define:

  • preview start
  • preview end
  • lock cue
  • reveal start
  • reveal end

19. Fairness and Clock Sync

Rule

The client may display a local countdown based on synced server time, but the server always decides final bet acceptance.

Requirements

  • regular server clock sync
  • client clock drift compensation
  • request tracing for all bet intents
  • strict idempotency keys
  • duplicate submissions must be safe

Logged timestamps

For all critical actions, capture:

  • client timestamp
  • server receive timestamp
  • request id
  • session id
  • app version
  • locale
  • device platform

20. Analytics and Telemetry

Log as many meaningful actions as possible to support later optimization, analysis, and UX refinement.

20.1 Core rule

All analytics events must be structured. Do not store opaque analytics blobs in JSONB. Use event tables and attribute tables.

20.2 Minimum tracked actions

  • app_opened
  • app_backgrounded
  • app_closed
  • language_selected
  • consent_viewed
  • consent_accepted
  • session_started
  • session_ended
  • feed_viewed
  • round_card_viewed
  • round_loaded
  • event_manifest_received
  • preview_prefetch_started
  • preview_prefetch_completed
  • preview_started
  • preview_paused
  • preview_resumed
  • preview_completed
  • countdown_visible
  • countdown_warning_threshold_hit
  • odds_panel_viewed
  • odds_version_received
  • odds_changed
  • outcome_focused
  • outcome_selected
  • selection_submitted
  • selection_accepted
  • selection_rejected
  • duplicate_selection_attempt
  • market_locked
  • reveal_started
  • reveal_completed
  • result_viewed
  • next_round_requested
  • session_paused
  • exit_prompt_viewed
  • exit_confirmed
  • playback_error
  • network_error
  • stream_reconnected
  • localization_bundle_loaded
  • locale_changed
  • haptic_triggered
  • gesture_swipe
  • gesture_tap
  • gesture_cancelled
  • screen_viewed
  • cta_pressed

20.3 Analytics event schema

Store:

  • event row in analytics_events
  • one row per attribute in analytics_event_attributes

Example attributes:

  • screen_name
  • event_id
  • market_id
  • outcome_id
  • odds_version_id
  • countdown_ms_remaining
  • locale_code
  • experiment_variant
  • network_type
  • device_orientation
  • playback_position_ms
  • latency_ms

20.4 Research metrics

  • decision latency
  • round completion rate
  • missed rounds
  • post-lock selection attempts
  • replay desire
  • exit rate
  • difference between control and modern
  • effect of locale
  • effect of device platform

21. Audit Logging

Audit logging is separate from analytics.

Audit goals

  • fairness
  • traceability
  • admin accountability
  • settlement trace

Audit examples

  • event created
  • market opened
  • odds version published
  • market locked
  • bet accepted
  • bet rejected
  • result settled
  • admin override applied
  • localization updated

Store:

  • audit_logs
  • audit_log_attributes

22. Security

  • signed tokens
  • strict input validation
  • rate limiting
  • request ids
  • full tracing
  • secrets outside the repo
  • environment-based config
  • server-side experiment assignment
  • server-side odds authority

23. Testing Strategy

Backend

  • unit tests per module
  • integration tests with PostgreSQL
  • SQLx compile-time query checks
  • state transition tests
  • API contract tests
  • migration tests

iOS

  • unit tests for view models and reducers
  • snapshot tests for core screens
  • UI tests for the full round flow
  • playback error tests
  • localization tests for English and Swedish

Android

  • unit tests for reducers and view models
  • Compose UI tests
  • macrobenchmark tests
  • playback tests
  • localization tests for English and Swedish
  • coroutine and Flow tests

End-to-end

  • start session
  • assign variant
  • load event
  • prefetch preview
  • start preview
  • submit selection
  • lock market
  • play reveal
  • settle result
  • ingest analytics

24. Performance Goals

Backend

  • p95 standard GET < 100 ms in staging-like conditions
  • p95 POST /bets/intent < 150 ms
  • low odds stream latency

Mobile

  • no visible jank in the round flow
  • very fast touch feedback
  • next round prefetched before the current one ends
  • low memory churn on screen transitions

25. CI and CD

Backend pipeline

  • rustfmt
  • clippy
  • tests
  • SQLx checks
  • migration validation
  • build container

iOS pipeline

  • lint
  • unit tests
  • UI tests
  • localization checks

Android pipeline

  • ktlint or equivalent
  • detekt if adopted
  • unit tests
  • Compose tests
  • benchmark checks where practical
  • localization checks

General

  • PR template
  • changelog
  • release tags
  • API contract versioning

26. Configuration Profiles

Minimum environments:

  • local
  • dev
  • staging
  • study
  • prod

Each environment must define:

  • backend URL
  • stream URL
  • CDN base URL
  • analytics mode
  • experiment config
  • localization source
  • log verbosity

27. Feature Flags

Must support flags for:

  • control vs modern
  • animation intensity
  • haptics enabled
  • autoplay next enabled or disabled
  • countdown style
  • odds update visualization
  • result card style
  • localization source mode

28. AI Agent Execution Plan

The AI agent must work in this order.

After each major completed change set, create a git commit and push it before starting the next major change.

Phase 1: Documents

  1. Write root README
  2. Write architecture document
  3. Write API spec
  4. Write relational schema
  5. Write SQLx migrations
  6. Write state machine spec
  7. Write localization key catalog
  8. Write analytics taxonomy

Phase 2: Backend foundation

  1. Initialize Rust workspace
  2. Add Axum, Tokio, SQLx, tracing, serde, config
  3. Add config system
  4. Add app state
  5. Add error handling
  6. Add health endpoint
  7. Connect PostgreSQL
  8. Add migrations
  9. Add Redis
  10. Add session endpoints

Phase 3: Domain and API

  1. Implement users
  2. Implement sessions
  3. Implement events
  4. Implement event media
  5. Implement markets
  6. Implement outcomes
  7. Implement odds versions
  8. Implement bet intents
  9. Implement settlement
  10. Implement experiments
  11. Implement localization
  12. Implement analytics

Phase 4: Fixtures and admin

  1. Add seed scripts
  2. Add fixture loading
  3. Add admin endpoints
  4. Add test event generator

Phase 5: iOS app

  1. Initialize project structure
  2. Build design system
  3. Build localization layer
  4. Build networking client
  5. Build feed screen
  6. Build round screen
  7. Integrate AVPlayer
  8. Build countdown and odds overlay
  9. Build selection flow
  10. Build reveal and result
  11. Add analytics

Phase 6: Android app

  1. Initialize project structure
  2. Build design system
  3. Build localization layer
  4. Build networking client
  5. Use Kotlin coroutines and Flow broadly
  6. Use Kotlinx Serialization for API models
  7. Use kotlinx-datetime for time handling
  8. Build feed screen
  9. Build round screen
  10. Integrate Media3
  11. Build countdown and odds overlay
  12. Build selection flow
  13. Build reveal and result
  14. Add analytics

Phase 7: Quality

  1. Add tests
  2. Validate localization
  3. benchmark critical paths
  4. review gesture edge cases
  5. validate analytics coverage
  6. document research export path

29. Definition of Done

A version is done when:

  • the user can start a session
  • a variant is assigned
  • the next event is loaded
  • preview video plays
  • odds are shown and updated
  • the user can select an outcome
  • the server accepts or rejects correctly
  • lock state is clear
  • reveal plays
  • result is shown
  • analytics are written relationally
  • English and Swedish both work
  • the flow passes end-to-end tests

30. Future Extensions

  • small shared Kotlin module for non-UI state logic
  • researcher admin web panel
  • analytics export to CSV or Parquet
  • accessibility pass
  • tablet and foldable layouts
  • richer localization management
  • advanced telemetry dashboards

31. Important Constraints

  • no real money flow in the first version
  • localization in English and Swedish is mandatory
  • normalized relational logging is mandatory
  • avoid JSONB in database design
  • log broadly enough to enable later UX optimization