From 17df545fba1002b4073e028c59d57a44941c13bd Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Tue, 18 Nov 2025 01:21:38 +0200 Subject: [PATCH] Add initial project design and specification documents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive design document with validated architecture - Add formal specification with RFC 2119 requirements language - Include MVVM architecture, testing strategy, and platform requirements - Define shake detection algorithm and estimate generation logic šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/plans/2025-11-18-hiha-arvio-design.md | 478 ++++++++++++++++ spec.md | 635 +++++++++++++++++++++ 2 files changed, 1113 insertions(+) create mode 100644 docs/plans/2025-11-18-hiha-arvio-design.md create mode 100644 spec.md diff --git a/docs/plans/2025-11-18-hiha-arvio-design.md b/docs/plans/2025-11-18-hiha-arvio-design.md new file mode 100644 index 0000000..9bdfd08 --- /dev/null +++ b/docs/plans/2025-11-18-hiha-arvio-design.md @@ -0,0 +1,478 @@ +# Hiha-Arvio (Sleeve Estimate) - Design Document + +**Date:** 2025-11-18 +**Project:** Hiha-Arvio +**Platforms:** iOS (primary), Web, macOS +**Technology:** .NET 8 MAUI, Blazor WebAssembly + +## Overview + +Hiha-Arvio (Finnish: "Sleeve Estimate") is a humor application that generates semi-random time estimates based on shake intensity. Users physically shake their phone (or mouse on desktop) to "pull an estimate from their sleeve" - a playful take on the Finnish expression for making educated guesses. + +### Key Features + +- **Dual estimation modes:** Work/project estimates and generic time durations (user-toggleable) +- **Intensity-based randomness:** Gentle shakes produce narrow, conservative ranges; vigorous shakes yield wide, unpredictable ranges +- **Hidden easter egg:** Shaking for >15 seconds triggers humorous/absurd estimates +- **Estimate history:** Last 10 estimates with timestamps (expandable for future stats) +- **Cross-platform:** Mobile-first (iOS), then web and macOS + +## Architecture + +### MVVM Pattern with Strict Separation + +**Core Principle:** All business logic resides in ViewModels and Services for 100% testability. Views are thin, data-bound only. + +### Models + +```csharp +EstimateResult +ā”œā”€ā”€ Id: Guid +ā”œā”€ā”€ Timestamp: DateTimeOffset +ā”œā”€ā”€ EstimateText: string // "2 weeks", "eventually" +ā”œā”€ā”€ Mode: EstimateMode // Work, Generic, Humorous +ā”œā”€ā”€ ShakeIntensity: double // 0.0 to 1.0 normalized +└── ShakeDuration: TimeSpan + +ShakeData +ā”œā”€ā”€ Intensity: double // Current shake strength +ā”œā”€ā”€ Duration: TimeSpan // How long shaking +└── IsShaking: bool + +AppSettings +ā”œā”€ā”€ SelectedMode: EstimateMode // Work or Generic toggle +└── MaxHistorySize: int // Default 10 +``` + +### Services (All Injectable) + +**IAccelerometerService** +- Abstracts platform accelerometer access (iOS) or mouse movement (desktop/web) +- Emits raw sensor data stream + +**IShakeDetectionService** +- Processes accelerometer stream +- Detects shake start/stop events +- Calculates normalized intensity (0.0-1.0) +- Tracks shake duration + +**IEstimateService** +- Generates estimates based on intensity, duration, and mode +- Implements easter egg logic (>15s → humorous mode) +- Manages estimate pools and range calculations + +**IStorageService** +- Persists AppSettings (Preferences API) +- Stores EstimateResult history (SQLite) +- Auto-prunes history to max size + +### ViewModels + +**MainViewModel** +- Orchestrates shake detection and estimate generation +- Commands: ShakeStarted, ShakeStopped +- Properties: CurrentEstimate, IsShaking, History +- Updates UI, saves estimates to storage + +**SettingsViewModel** +- Manages mode toggle and future preferences +- Persists settings changes + +### Views + +**MainPage** +- Large shake detection zone (80% screen) +- Minimal UI: pulsing animation during shake +- Estimate display with fade-in animation +- Mode toggle at bottom +- Settings/History icons in top bar + +**HistoryPage** +- Scrollable list of last 10 estimates +- Each item: estimate text, timestamp, mode badge, intensity indicator +- Pull-to-refresh support + +**SettingsPage** +- Mode toggle (Work ⟷ Generic) +- Future: sensitivity adjustment, max history size +- About/version info + +## Data Flow + +1. User shakes device → `IAccelerometerService` emits sensor data +2. `IShakeDetectionService` processes stream → detects shake, calculates intensity +3. On shake stop → `MainViewModel` invokes `IEstimateService.GenerateEstimate(intensity, duration, mode)` +4. Service checks easter egg (duration > 15s → force humorous mode) +5. Service calculates estimate range based on intensity +6. Returns random `EstimateResult` from calculated range +7. `MainViewModel` updates UI and calls `IStorageService.SaveEstimate()` +8. Storage auto-prunes history if exceeding max size + +## Shake Detection Algorithm + +### Accelerometer Processing (iOS) + +```csharp +1. Monitor accelerometer stream via Microsoft.Maui.Devices.Sensors.Accelerometer +2. For each reading (x, y, z): + magnitude = sqrt(x² + y² + z²) +3. Shake start: magnitude > 2.5g threshold +4. Track peak magnitude during shake session +5. Shake end: magnitude < threshold for 500ms continuous +6. Normalize intensity: peak / max_observed → [0.0, 1.0] +``` + +### Mouse Movement Simulation (Desktop/Web) + +```csharp +1. Track mouse delta (Ī”x, Ī”y) over sliding 200ms window +2. Calculate velocity: sqrt(Ī”x² + Ī”y²) / Ī”t +3. "Shake" detection: velocity exceeds threshold +4. Intensity: normalize velocity relative to max observed +``` + +## Estimate Calculation Logic + +### Estimate Pools + +**Work Mode (gentle shake):** +- Range: 2 hours, 4 hours, 1 day, 2 days, 3 days, 5 days, 1 week + +**Work Mode (hard shake):** +- Range: 15 minutes, 30 minutes, 1 hour, 2 hours, 1 day, 3 days, 1 week, 2 weeks, 1 month, 3 months, 6 months, 1 year + +**Generic Mode (gentle shake):** +- Range: 1 minute, 5 minutes, 10 minutes, 15 minutes, 30 minutes, 1 hour, 2 hours, 3 hours + +**Generic Mode (hard shake):** +- Range: 30 seconds, 1 minute, 5 minutes, 15 minutes, 30 minutes, 1 hour, 2 hours, 6 hours, 12 hours, 1 day, 3 days, 1 week, 2 weeks, 1 month + +**Humorous Mode (easter egg):** +- Mix: "5 minutes", "tomorrow", "eventually", "next quarter", "when hell freezes over", "3 lifetimes", "Tuesday", "never", "your retirement" + +### Range Selection Algorithm + +```csharp +GenerateEstimate(intensity, duration, mode): + 1. IF duration > 15 seconds THEN mode = Humorous + + 2. SELECT estimate_pool based on mode + + 3. CALCULATE range bounds: + IF intensity < 0.3 THEN + range = first 20% of pool (conservative) + ELSE IF intensity < 0.7 THEN + range = first 50% of pool (moderate) + ELSE + range = entire pool (wild) + + 4. SELECT random estimate from range + + 5. RETURN EstimateResult with metadata +``` + +## Data Persistence + +### Storage Implementation + +**Technology:** +- SQLite (via `sqlite-net-pcl`) for EstimateResult history +- Preferences API for AppSettings + +**Schema:** +```sql +CREATE TABLE EstimateHistory ( + Id TEXT PRIMARY KEY, + Timestamp INTEGER NOT NULL, + EstimateText TEXT NOT NULL, + Mode INTEGER NOT NULL, + ShakeIntensity REAL NOT NULL, + ShakeDuration INTEGER NOT NULL +); + +CREATE INDEX idx_timestamp ON EstimateHistory(Timestamp DESC); +``` + +**Operations:** +- `SaveSettings(AppSettings)` - Immediate persist to Preferences +- `LoadSettings()` - On app start +- `SaveEstimate(EstimateResult)` - After each shake, auto-prune if > max size +- `GetHistory(int count = 10)` - Return newest first +- `ClearHistory()` - For future reset feature + +### Platform Storage Locations + +- **iOS:** App's Documents directory +- **Web:** LocalStorage (settings) + IndexedDB (history) +- **macOS:** Application Support directory + +## UI/UX Design + +### Visual Design + +**Color Scheme:** Clean, playful, high contrast +**Typography:** Large, bold estimates for readability +**Animations:** Smooth, 200-300ms transitions + +### MainPage Details + +- **Shake zone:** 80% of screen height, touch/click anywhere to activate +- **Active shake:** Subtle scale pulse animation (1.0 → 1.05 → 1.0 at 2Hz) +- **Estimate reveal:** Fade in + slight slide up +- **Mode toggle:** Bottom-aligned, labeled "Work Estimates ⟷ Generic Time" +- **Top bar:** Settings (gear icon), History (clock icon) + +### HistoryPage Details + +- **List items:** + - Estimate text (large, bold) + - Relative timestamp ("2 minutes ago") + - Mode badge (color-coded: blue=Work, green=Generic, orange=Humorous) + - Intensity indicator (3/5 filled dots) +- **Empty state:** "No estimates yet. Give it a shake!" +- **Pull-to-refresh:** Reload from storage + +### Accessibility + +- Full keyboard navigation support +- VoiceOver/TalkBack descriptions +- Minimum touch target: 44x44 points +- WCAG AA contrast ratios + +## Testing Strategy + +### Target Coverage: ~100% + +### Unit Tests (xUnit + NSubstitute) + +**Services:** +- `EstimateServiceTests` + - Verify intensity → range mapping + - Test each mode's estimate pools + - Validate easter egg trigger (>15s) + - Check randomness bounds + - Edge cases: 0.0 intensity, 1.0 intensity + +- `ShakeDetectionServiceTests` + - Magnitude calculation accuracy + - Threshold detection (start/stop) + - Intensity normalization + - Duration tracking + +- `StorageServiceTests` + - CRUD operations + - History auto-pruning + - Settings persistence + - Concurrent access handling + +**ViewModels:** +- `MainViewModelTests` + - Command execution + - Property change notifications + - Service interaction mocking + - History updates + +- `SettingsViewModelTests` + - Setting changes trigger persistence + - Mode toggle behavior + +### Integration Tests + +- Test full data flow: ShakeDetection → EstimateService → Storage +- Mock only platform-specific dependencies (accelerometer) +- Verify ViewModel + Services integration + +### UI Tests (Appium - Future) + +- Critical path: Shake gesture → Estimate appears → Check history +- Mode toggle switches estimate pool +- Platform-specific: iOS shake, web mouse movement + +### Test Data Builders + +```csharp +EstimateResultBuilder + .WithMode(EstimateMode.Work) + .WithIntensity(0.5) + .Build(); + +ShakeDataBuilder + .WithIntensity(0.8) + .WithDuration(TimeSpan.FromSeconds(5)) + .Build(); +``` + +### Deterministic Testing + +- Seed RNG in tests for reproducible randomness +- Time-based tests use injectable `ITimeProvider` + +## Platform-Specific Implementation + +### iOS (Primary Platform) + +- **Accelerometer:** `Microsoft.Maui.Devices.Sensors.Accelerometer` +- **Permissions:** Add motion usage description to Info.plist +- **Background behavior:** Pause monitoring when backgrounded +- **Native feel:** iOS design patterns, haptic feedback on shake +- **Target:** iOS 15+ (MAUI .NET 8 requirement) + +### Web (Blazor WebAssembly) + +- **Mouse simulation:** Track delta movement, calculate velocity +- **Shake zone:** Visual boundary for mouse movement +- **Alternative:** Device Orientation API for mobile browsers +- **PWA:** Progressive Web App manifest for home screen install +- **Responsive:** Works on mobile browsers and desktops + +### macOS + +- **Input:** Mouse movement tracking (similar to web) +- **Native chrome:** macOS window style and menu bar +- **Keyboard shortcut:** Cmd+Shift+S to trigger manual shake +- **Accessibility:** Full keyboard navigation + +### Dependency Injection Registration + +```csharp +// MauiProgram.cs +#if IOS +builder.Services.AddSingleton(); +#elif WINDOWS || MACCATALYST +builder.Services.AddSingleton(); +#endif + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +``` + +## Project Structure + +``` +HihaArvio.sln +ā”œā”€ā”€ src/ +│ ā”œā”€ā”€ HihaArvio/ # Main MAUI project +│ │ ā”œā”€ā”€ Models/ +│ │ │ ā”œā”€ā”€ EstimateResult.cs +│ │ │ ā”œā”€ā”€ ShakeData.cs +│ │ │ └── AppSettings.cs +│ │ ā”œā”€ā”€ ViewModels/ +│ │ │ ā”œā”€ā”€ MainViewModel.cs +│ │ │ └── SettingsViewModel.cs +│ │ ā”œā”€ā”€ Views/ +│ │ │ ā”œā”€ā”€ MainPage.xaml +│ │ │ ā”œā”€ā”€ HistoryPage.xaml +│ │ │ └── SettingsPage.xaml +│ │ ā”œā”€ā”€ Services/ +│ │ │ ā”œā”€ā”€ Interfaces/ +│ │ │ │ ā”œā”€ā”€ IAccelerometerService.cs +│ │ │ │ ā”œā”€ā”€ IShakeDetectionService.cs +│ │ │ │ ā”œā”€ā”€ IEstimateService.cs +│ │ │ │ └── IStorageService.cs +│ │ │ ā”œā”€ā”€ EstimateService.cs +│ │ │ ā”œā”€ā”€ ShakeDetectionService.cs +│ │ │ ā”œā”€ā”€ StorageService.cs +│ │ │ └── Platform/ +│ │ │ ā”œā”€ā”€ IOSAccelerometerService.cs +│ │ │ └── MouseMovementService.cs +│ │ ā”œā”€ā”€ MauiProgram.cs +│ │ └── App.xaml +│ ā”œā”€ā”€ HihaArvio.Core/ # Shared business logic (optional) +│ └── HihaArvio.Web/ # Blazor WebAssembly (future) +└── tests/ + ā”œā”€ā”€ HihaArvio.Tests/ # Unit tests + ā”œā”€ā”€ HihaArvio.IntegrationTests/ # Integration tests + └── HihaArvio.UITests/ # UI automation (future) +``` + +## Dependencies + +### Core Dependencies +- `Microsoft.Maui.Controls` (.NET 8) +- `CommunityToolkit.Mvvm` (Source generators, MVVM helpers) +- `sqlite-net-pcl` (Database) +- `SQLitePCLRaw.bundle_green` (SQLite runtime) + +### Testing Dependencies +- `xUnit` (Test framework) +- `NSubstitute` (Mocking) +- `FluentAssertions` (Assertion library) +- `Coverlet.Collector` (Code coverage) +- `Appium.WebDriver` (UI tests - future) + +### Development Tools +- `ReportGenerator` (Coverage reports) +- `StyleCop.Analyzers` (Code analysis) + +## Build & Deployment + +### CI/CD Pipeline (GitHub Actions) + +```yaml +1. Build .NET MAUI solution +2. Run unit tests +3. Run integration tests +4. Collect code coverage (Coverlet) +5. Generate coverage report (ReportGenerator) +6. Enforce coverage threshold: 95%+ +7. Build iOS app (on macOS runner) +8. Build web app (publish Blazor) +``` + +### Code Quality + +- **Nullable reference types:** Enabled +- **Warnings as errors:** Enabled +- **Code analysis:** StyleCop + built-in analyzers +- **EditorConfig:** Consistent formatting rules + +### Release Strategy + +- **Phase 1:** iOS app (TestFlight beta) +- **Phase 2:** Web app (static hosting) +- **Phase 3:** macOS app (App Store) + +## Future Enhancements + +### Stats Dashboard (Post-MVP) +- Most common estimate +- Average shake intensity +- Total shakes count +- Humorous mode discovery rate + +### Advanced Features +- Custom estimate pools (user-defined) +- Sensitivity adjustment slider +- Share estimate to clipboard/social +- Team mode (synchronized shaking) + +### Localization +- Finnish translation (native language) +- English (default) +- Extensible for more languages + +## Success Criteria + +- āœ… Test coverage ≄95% +- āœ… iOS app functions on iOS 15+ +- āœ… Shake detection feels responsive (<100ms latency) +- āœ… Estimate generation is visually randomized +- āœ… History persists across app restarts +- āœ… Easter egg discoverable but not obvious +- āœ… All three platforms build without errors + +## Technical Risks & Mitigations + +| Risk | Mitigation | +|------|------------| +| Accelerometer sensitivity varies by device | Adaptive normalization, user calibration option | +| Web mouse simulation feels unnatural | Add tutorial/demo, support touch on mobile web | +| SQLite performance with large history | Auto-prune, indexed queries, async operations | +| .NET MAUI platform quirks | Thorough platform-specific testing, community resources | +| Test coverage hard to reach 100% on Views | Keep Views minimal, test ViewModels exhaustively | + +--- + +**Document Status:** Validated +**Next Steps:** Create formal specification, begin implementation planning diff --git a/spec.md b/spec.md new file mode 100644 index 0000000..a5369dd --- /dev/null +++ b/spec.md @@ -0,0 +1,635 @@ +# Hiha-Arvio Application Specification + +**Version:** 1.0 +**Date:** 2025-11-18 +**Status:** Draft + +## 1. Introduction + +This document specifies the requirements for Hiha-Arvio (Sleeve Estimate), a cross-platform mobile and web application that generates semi-random time estimates based on physical shake input. + +### 1.1 Terminology + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). + +### 1.2 Scope + +This specification covers: +- Functional requirements for shake detection and estimate generation +- Data persistence and history management +- User interface requirements +- Platform-specific implementations +- Testing and quality requirements + +## 2. Platform Requirements + +### 2.1 Technology Stack + +The application MUST be implemented using the following technologies: + +- **Framework:** .NET 8 Multi-platform App UI (MAUI) +- **Language:** C# 12 with nullable reference types enabled +- **Minimum iOS version:** iOS 15.0 +- **Minimum macOS version:** macOS 12.0 (Monterey) +- **Web target:** Blazor WebAssembly + +### 2.2 Supported Platforms + +The application MUST support the following platforms in order of priority: + +1. **iOS** (PRIMARY) - Native iOS application +2. **Web** (SECONDARY) - Browser-based via Blazor WebAssembly +3. **macOS** (TERTIARY) - Native macOS application + +The application MAY support additional platforms (Android, Windows) in future releases. + +## 3. Functional Requirements + +### 3.1 Shake Detection + +#### 3.1.1 Mobile Platforms (iOS) + +The application MUST: +- Monitor device accelerometer data using `Microsoft.Maui.Devices.Sensors.Accelerometer` +- Calculate shake magnitude as `sqrt(x² + y² + z²)` for each accelerometer reading +- Detect shake start when magnitude exceeds 2.5g threshold +- Track peak magnitude during entire shake session +- Detect shake end when magnitude remains below threshold for 500 milliseconds continuous +- Normalize shake intensity to range [0.0, 1.0] based on peak magnitude relative to maximum observed + +The application SHOULD: +- Request motion sensor permissions on first launch +- Pause accelerometer monitoring when app is backgrounded +- Provide haptic feedback when shake is detected + +#### 3.1.2 Desktop Platforms (Web, macOS) + +The application MUST: +- Monitor mouse movement delta (Ī”x, Ī”y) over a 200-millisecond sliding window +- Calculate mouse velocity as `sqrt(Ī”x² + Ī”y²) / Ī”t` +- Detect "shake" when velocity exceeds platform-appropriate threshold +- Normalize shake intensity based on velocity relative to maximum observed + +The application SHOULD: +- Display visual boundaries for the shake detection zone +- Provide alternative keyboard shortcut (Cmd+Shift+S on macOS) for manual shake trigger + +The application MAY: +- Support Device Orientation API for mobile web browsers as alternative input method + +### 3.2 Estimate Generation + +#### 3.2.1 Estimation Modes + +The application MUST support three estimation modes: + +1. **Work Mode** - Project and work-related time estimates +2. **Generic Mode** - General-purpose duration estimates +3. **Humorous Mode** - Absurd and exaggerated estimates (easter egg) + +The application MUST: +- Provide user interface toggle between Work Mode and Generic Mode +- Keep Humorous Mode hidden (no UI control) +- Activate Humorous Mode automatically when shake duration exceeds 15 seconds +- Persist user's mode selection (Work/Generic) across app sessions + +#### 3.2.2 Estimate Pools + +The application MUST maintain the following estimate pools: + +**Work Mode Estimates:** +- Gentle shake pool: "2 hours", "4 hours", "1 day", "2 days", "3 days", "5 days", "1 week" +- Hard shake pool: "15 minutes", "30 minutes", "1 hour", "2 hours", "1 day", "3 days", "1 week", "2 weeks", "1 month", "3 months", "6 months", "1 year" + +**Generic Mode Estimates:** +- Gentle shake pool: "1 minute", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour", "2 hours", "3 hours" +- Hard shake pool: "30 seconds", "1 minute", "5 minutes", "15 minutes", "30 minutes", "1 hour", "2 hours", "6 hours", "12 hours", "1 day", "3 days", "1 week", "2 weeks", "1 month" + +**Humorous Mode Estimates:** +- "5 minutes", "tomorrow", "eventually", "next quarter", "when hell freezes over", "3 lifetimes", "Tuesday", "never", "your retirement" + +The application MAY expand these pools in future versions. + +#### 3.2.3 Estimate Selection Algorithm + +The application MUST implement the following algorithm: + +``` +FUNCTION GenerateEstimate(intensity: double, duration: TimeSpan, mode: EstimateMode): + 1. IF duration > 15 seconds THEN + SET mode = Humorous + + 2. SELECT estimate_pool based on current mode + + 3. CALCULATE range_bounds: + IF intensity < 0.3 THEN + range = first 20% of estimate_pool + ELSE IF intensity < 0.7 THEN + range = first 50% of estimate_pool + ELSE + range = entire estimate_pool + + 4. SELECT random estimate from range using cryptographically secure RNG + + 5. RETURN EstimateResult with: + - Selected estimate text + - Current timestamp + - Active mode + - Normalized intensity + - Shake duration +``` + +The application MUST use cryptographically secure random number generation for estimate selection to ensure unpredictability. + +### 3.3 History Management + +#### 3.3.1 Storage Requirements + +The application MUST: +- Persist the last 10 estimate results (REQUIRED minimum) +- Store each estimate with complete metadata: timestamp, estimate text, mode, intensity, duration +- Save estimates immediately after generation +- Load history on application startup +- Automatically prune oldest estimates when count exceeds maximum (10) + +The application SHOULD: +- Store history in SQLite database using `sqlite-net-pcl` library +- Create timestamp index for efficient queries +- Perform all database operations asynchronously + +The application MAY: +- Allow users to configure maximum history size (future enhancement) +- Export history to CSV or JSON format (future enhancement) + +#### 3.3.2 Settings Persistence + +The application MUST: +- Persist user's selected mode (Work/Generic) across sessions +- Load saved settings on application startup +- Use platform-native Preferences API for settings storage + +### 3.4 User Interface + +#### 3.4.1 Main Page + +The application MUST display: +- Large shake detection zone occupying at least 80% of screen height +- Current estimate result with large, readable typography +- Mode toggle control labeled "Work Estimates ⟷ Generic Time" +- Navigation icons for Settings and History pages + +The application MUST provide visual feedback: +- Subtle pulsing animation (scale 1.0 → 1.05 → 1.0) during active shake +- Smooth fade-in animation for estimate reveal (200-300ms duration) +- No visual timer or indicator for 15-second easter egg + +The application MUST NOT: +- Display loading spinners or progress bars during shake +- Show shake intensity meter or real-time feedback +- Reveal the existence of Humorous Mode in UI + +#### 3.4.2 History Page + +The application MUST display for each history entry: +- Estimate text (large, bold typography) +- Relative timestamp (e.g., "2 minutes ago", "1 hour ago") +- Mode badge indicating Work/Generic/Humorous with color coding +- Visual intensity indicator (e.g., filled dots representing intensity level) + +The application MUST: +- Display estimates in reverse chronological order (newest first) +- Show empty state message when history is empty: "No estimates yet. Give it a shake!" +- Support pull-to-refresh gesture for reloading + +The application SHOULD: +- Limit visible history to 10 most recent entries +- Provide smooth scrolling for list navigation + +#### 3.4.3 Settings Page + +The application MUST provide: +- Mode toggle control (mirroring Main Page toggle) +- Application version information +- About section with attribution + +The application MAY provide: +- Sensitivity adjustment controls (future enhancement) +- Maximum history size configuration (future enhancement) +- Reset/clear history button (future enhancement) + +#### 3.4.4 Accessibility + +The application MUST: +- Support full keyboard navigation on desktop platforms +- Provide VoiceOver/TalkBack descriptions for all interactive elements +- Maintain minimum touch target size of 44x44 points +- Meet WCAG 2.1 Level AA contrast ratio requirements (4.5:1 for normal text) + +The application SHOULD: +- Support Dynamic Type on iOS +- Respect user's reduced motion preferences +- Provide alternative text for all icons + +## 4. Architecture Requirements + +### 4.1 Design Pattern + +The application MUST implement Model-View-ViewModel (MVVM) pattern with strict separation: + +- **Models** MUST be plain data objects with no business logic +- **ViewModels** MUST contain all presentation logic and be fully testable without UI +- **Views** MUST be thin, containing only data binding and minimal presentation code +- **Services** MUST encapsulate all business logic and infrastructure concerns + +### 4.2 Dependency Injection + +The application MUST: +- Use dependency injection for all service dependencies +- Register all services in `MauiProgram.cs` startup configuration +- Define service interfaces in `Services/Interfaces/` directory +- Inject dependencies via constructor injection + +The application MUST NOT: +- Use service locator pattern +- Create service instances with `new` keyword in ViewModels +- Use static dependencies + +### 4.3 Data Models + +The application MUST define the following models: + +```csharp +public class EstimateResult +{ + public Guid Id { get; set; } // REQUIRED + public DateTimeOffset Timestamp { get; set; } // REQUIRED + public string EstimateText { get; set; } // REQUIRED + public EstimateMode Mode { get; set; } // REQUIRED + public double ShakeIntensity { get; set; } // REQUIRED, range [0.0, 1.0] + public TimeSpan ShakeDuration { get; set; } // REQUIRED +} + +public enum EstimateMode +{ + Work, // REQUIRED + Generic, // REQUIRED + Humorous // REQUIRED +} + +public class ShakeData +{ + public double Intensity { get; set; } // REQUIRED, range [0.0, 1.0] + public TimeSpan Duration { get; set; } // REQUIRED + public bool IsShaking { get; set; } // REQUIRED +} + +public class AppSettings +{ + public EstimateMode SelectedMode { get; set; } // REQUIRED, default: Work + public int MaxHistorySize { get; set; } // REQUIRED, default: 10 +} +``` + +### 4.4 Service Interfaces + +The application MUST implement the following service contracts: + +```csharp +public interface IAccelerometerService +{ + IObservable DataStream { get; } // REQUIRED + void Start(); // REQUIRED + void Stop(); // REQUIRED +} + +public interface IShakeDetectionService +{ + IObservable ShakeStream { get; } // REQUIRED + void StartMonitoring(); // REQUIRED + void StopMonitoring(); // REQUIRED +} + +public interface IEstimateService +{ + EstimateResult GenerateEstimate( // REQUIRED + double intensity, + TimeSpan duration, + EstimateMode mode + ); +} + +public interface IStorageService +{ + Task SaveSettings(AppSettings settings); // REQUIRED + Task LoadSettings(); // REQUIRED + Task SaveEstimate(EstimateResult result); // REQUIRED + Task> GetHistory(int count = 10); // REQUIRED + Task ClearHistory(); // REQUIRED +} +``` + +## 5. Testing Requirements + +### 5.1 Code Coverage + +The application MUST achieve minimum 95% code coverage across all projects. + +The application MUST measure coverage using: +- Coverlet for .NET code coverage collection +- ReportGenerator for coverage report generation + +The application MUST enforce coverage thresholds in CI/CD pipeline and MUST fail builds that fall below 95%. + +### 5.2 Unit Testing + +The application MUST provide unit tests for: +- All service implementations (`EstimateService`, `ShakeDetectionService`, `StorageService`) +- All ViewModels (`MainViewModel`, `SettingsViewModel`) +- All public methods in models (if containing logic) +- All estimate selection algorithm branches + +The application MUST use: +- xUnit as testing framework +- NSubstitute for mocking dependencies +- FluentAssertions for assertion syntax + +The application MUST: +- Mock all external dependencies (file system, database, sensors) +- Use test data builders for complex object creation +- Seed random number generators for deterministic tests +- Test all edge cases (null inputs, boundary values, invalid states) + +### 5.3 Integration Testing + +The application MUST provide integration tests for: +- Complete data flow from shake detection through estimate generation to storage +- ViewModel and multiple service interactions +- Database operations with actual SQLite instance + +The application SHOULD: +- Use in-memory SQLite for faster test execution +- Parallelize integration tests where possible + +### 5.4 UI Testing + +The application SHOULD provide UI automation tests for: +- Critical user path: Shake → View estimate → Check history +- Mode toggle functionality +- History page display and navigation + +The application MAY use Appium WebDriver for cross-platform UI testing. + +## 6. Quality Requirements + +### 6.1 Code Quality + +The application MUST: +- Enable nullable reference types across all projects +- Treat all compiler warnings as errors +- Pass all enabled code analysis rules (StyleCop, built-in analyzers) +- Follow EditorConfig formatting rules + +The application MUST NOT: +- Suppress warnings without documented justification +- Disable code analysis rules without review +- Commit code with compiler errors + +### 6.2 Performance + +The application MUST: +- Respond to shake detection within 100 milliseconds +- Display estimate result within 200 milliseconds after shake stop +- Load history page within 500 milliseconds +- Complete all database operations asynchronously + +The application SHOULD: +- Minimize memory allocations during shake detection loop +- Cache estimate pools in memory +- Use lazy loading for history page + +### 6.3 Error Handling + +The application MUST: +- Handle sensor unavailability gracefully (show user-friendly message) +- Catch and log all exceptions in services +- Prevent crash from any single component failure +- Validate all user inputs + +The application SHOULD: +- Implement retry logic for transient database errors +- Log errors to platform-specific logging systems + +## 7. Platform-Specific Requirements + +### 7.1 iOS Requirements + +The application MUST: +- Target iOS 15.0 or higher +- Request motion sensor permissions via Info.plist `NSMotionUsageDescription` +- Pause sensor monitoring when app enters background +- Support iOS dark mode +- Follow iOS Human Interface Guidelines + +The application SHOULD: +- Provide haptic feedback on shake detection +- Support iPad multitasking and split view + +### 7.2 Web Requirements + +The application MUST: +- Support modern browsers: Chrome 90+, Safari 14+, Firefox 88+, Edge 90+ +- Implement responsive design for mobile and desktop viewports +- Function without server-side dependencies (static hosting) + +The application SHOULD: +- Provide Progressive Web App (PWA) manifest for home screen install +- Cache application assets for offline functionality +- Support touch gestures on mobile browsers + +The application MAY: +- Use Device Orientation API for mobile browser shake detection + +### 7.3 macOS Requirements + +The application MUST: +- Target macOS 12.0 (Monterey) or higher +- Follow macOS Human Interface Guidelines +- Support native window chrome and menu bar +- Implement keyboard shortcut Cmd+Shift+S for shake trigger + +The application SHOULD: +- Support macOS dark mode and accent colors +- Integrate with macOS accessibility features + +## 8. Security Requirements + +The application MUST: +- Use cryptographically secure random number generation (System.Security.Cryptography.RandomNumberGenerator) +- Store all data locally on device (no cloud transmission) +- Request minimum required permissions + +The application MUST NOT: +- Transmit any user data to external servers +- Store sensitive personal information +- Access device sensors without explicit permission + +## 9. Build and Deployment Requirements + +### 9.1 Build Configuration + +The application MUST: +- Define separate build configurations for Debug, Release, and Testing +- Enable code optimization for Release builds +- Include debug symbols for all builds +- Version assemblies using semantic versioning (SemVer) + +### 9.2 Continuous Integration + +The application MUST: +- Execute automated builds on every commit +- Run all unit and integration tests in CI pipeline +- Enforce code coverage thresholds (≄95%) +- Generate coverage reports for each build + +The application SHOULD: +- Use GitHub Actions for CI/CD automation +- Cache NuGet packages for faster builds +- Parallelize test execution + +### 9.3 Release Process + +The application MUST: +- Follow phased release approach: iOS → Web → macOS +- Increment version number for each release +- Tag releases in version control +- Generate release notes from commit history + +## 10. Documentation Requirements + +The application MUST provide: +- README.md with build instructions and prerequisites +- Architecture decision records (ADR) for significant technical choices +- API documentation for all public interfaces (XML comments) +- User guide covering all features + +The application SHOULD provide: +- Contribution guidelines for external contributors +- Code style guide referencing EditorConfig rules +- Troubleshooting guide for common issues + +## 11. Dependencies + +### 11.1 Required Dependencies + +The application MUST use the following NuGet packages: + +- `Microsoft.Maui.Controls` (≄8.0.0, <9.0.0) +- `CommunityToolkit.Mvvm` (≄8.2.0) +- `sqlite-net-pcl` (≄1.9.0) +- `SQLitePCLRaw.bundle_green` (≄2.1.0) + +### 11.2 Testing Dependencies + +The application MUST use the following packages for testing: + +- `xUnit` (≄2.6.0) +- `NSubstitute` (≄5.1.0) +- `FluentAssertions` (≄6.12.0) +- `Coverlet.Collector` (≄6.0.0) + +### 11.3 Dependency Management + +The application MUST: +- Pin major versions to prevent breaking changes +- Review and update dependencies quarterly +- Address security vulnerabilities within 30 days of disclosure + +The application MUST NOT: +- Use pre-release or beta packages in production builds +- Include unnecessary dependencies + +## 12. Compliance and Standards + +### 12.1 Coding Standards + +The application MUST adhere to: +- C# coding conventions (Microsoft official style guide) +- .NET naming conventions +- EditorConfig rules defined in repository + +### 12.2 Accessibility Standards + +The application MUST comply with: +- WCAG 2.1 Level AA +- Section 508 (U.S. accessibility requirements) +- iOS Accessibility Guidelines +- macOS Accessibility Guidelines + +## 13. Future Enhancements + +The following features MAY be implemented in future versions: + +### 13.1 Statistics Dashboard +- Display aggregate usage statistics +- Track most common estimates +- Show humorous mode discovery rate +- Calculate average shake intensity + +### 13.2 Customization +- Allow user-defined estimate pools +- Configurable sensitivity thresholds +- Custom color themes + +### 13.3 Social Features +- Share estimates to clipboard +- Export history as text/image +- Team synchronization mode + +### 13.4 Localization +- Finnish translation (fi-FI) +- Additional language support + +## 14. Acceptance Criteria + +For the application to be considered complete and ready for release, it MUST: + +- āœ… Build successfully on all target platforms without errors +- āœ… Pass all automated tests with ≄95% code coverage +- āœ… Function correctly on iOS 15+ devices with accurate shake detection +- āœ… Persist settings and history across app restarts +- āœ… Display estimates within 200ms of shake completion +- āœ… Correctly implement easter egg (15-second humorous mode) +- āœ… Meet WCAG 2.1 Level AA accessibility standards +- āœ… Complete security review with no high/critical vulnerabilities +- āœ… Pass manual testing on representative devices +- āœ… Include complete user and developer documentation + +## 15. Glossary + +- **Shake Intensity:** Normalized measure of shake strength, range [0.0, 1.0] +- **Shake Duration:** Time elapsed from shake start to shake stop +- **Easter Egg:** Hidden humorous mode triggered by prolonged shaking (>15 seconds) +- **Estimate Pool:** Predefined collection of possible time estimates for a given mode +- **Gentle Shake:** Shake with intensity < 0.3 +- **Hard Shake:** Shake with intensity ≄ 0.7 +- **LTS:** Long-Term Support (.NET version with extended support period) + +--- + +**Document Version:** 1.0 +**Last Updated:** 2025-11-18 +**Status:** Draft - Pending Implementation +**Prepared by:** Claude Code +**Approved by:** [Pending] + +--- + +## Revision History + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2025-11-18 | Claude Code | Initial specification document | + +--- + +**END OF SPECIFICATION**