Files
zapstore/spec/features/FEAT-001-package-manager.md
T
alltheseasandClaude Opus 4.5 958841eebc fix: address code review feedback
1. main.dart: Add database migration from Documents to Support directory
   - Migrates existing zapstore.db to new location on first run
   - Preserves existing users' data after directory change

2. installed_packages_snapshot.dart: Use atomic file replace
   - Remove explicit delete before rename (rename atomically replaces)
   - Add try/catch to preserve temp file on rename failure

3. polls_section.dart: Fix useEffect cleanup for dynamic controllers
   - Capture controller snapshot in useEffect
   - Properly dispose controllers added via "Add Option"

4. polls_section.dart: Fix filter to include votes at exact end time
   - Changed from isBefore to !isAfter for inclusive filtering

5. polls_utils.dart: Extract shared utility functions
   - canCreatePoll, filterValidResponses, calculateVoteCounts
   - Marked @visibleForTesting for test access

6. FEAT-001-package-manager.md: Add markdown language specifiers
   - Fix MD040 lint warnings for code blocks

7. polls_section_test.dart: Use production utils instead of duplicates
   - Import and test actual canCreatePoll from polls_utils
   - Add test for votes at exact poll end time

Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 11:31:44 -06:00

5.1 KiB

FEAT-001 — Package Manager

Goal

Single source of truth for installed packages and active install operations. Manages the complete lifecycle: download → verify → install, with pause/resume/cancel support.

Non-Goals

  • Managing non-APK file types
  • Auto-updating without user awareness
  • Installing from sources other than Nostr-published releases

User-Visible Behavior

Download Phase

  • User taps "Install" → download begins, progress shown
  • User can pause/resume/cancel active downloads
  • Multiple downloads queue automatically (max 3 concurrent)
  • "Update All" queues all updates immediately with visual feedback

Verification Phase

  • After download completes, hash verification runs
  • Verification state is visible (not hidden)
  • Hash mismatch blocks install with clear error

Permission Phase

  • If "Install unknown apps" permission not granted, user is prompted
  • Permission state is explicit in UI
  • Once granted, all waiting installs advance automatically

Install Phase

  • Native Android install dialog shown
  • One install dialog at a time (serialized)
  • If user dismisses dialog, install shows "Tap to retry" state
  • Success updates installed list immediately (no stale UI)

Failure States

  • Download failed → clear error, can retry
  • Hash mismatch → error, cannot proceed
  • Certificate mismatch → offer "Uninstall and reinstall" option
  • Permission denied → guidance to enable in Settings

State Machine

Operations follow this sealed class hierarchy (install_operation.dart):

DownloadQueued → Downloading ↔ DownloadPaused
                      ↓
                 Verifying
                      ↓
              AwaitingPermission (if needed)
                      ↓
               ReadyToInstall
                      ↓
                Installing → AwaitingUserAction (if dismissed)
                      ↓
             [cleared] or OperationFailed

State transitions are unidirectional except Downloading ↔ DownloadPaused.

Edge Cases

  • Network drops mid-download → download pauses or fails gracefully, can retry
  • App backgrounded during install → install completes, UI updates on return
  • 404 from origin server → automatic CDN fallback before failing
  • Stale operations (>7 days) → garbage collected on app restart
  • Android package DB race condition → state updated from target metadata, not sync

Invariants

These are non-negotiable. Violations mean the implementation is broken.

  1. UI never blocks — install() returns immediately; events drive state via EventChannel
  2. One install dialog at a time — Android PackageInstaller limitation, enforced by serialization
  3. Hash verification before install — Native side verifies before install session opens
  4. Permission flow is explicit — AwaitingPermission state exists for UI feedback
  5. Downloaded files are cleaned up — Deleted after success or dismissal
  6. No polling — All state changes via callbacks/events, never periodic checks

Integration Boundaries

┌─────────────────────────────────────────────────────────────┐
│ PackageManager (Dart)                                       │
│   - State machine owner                                     │
│   - Download management (background_downloader)             │
│   - Orchestrates flow                                       │
└─────────────────────────┬───────────────────────────────────┘
                          │ MethodChannel / EventChannel
┌─────────────────────────▼───────────────────────────────────┐
│ AndroidPackageManagerPlugin (Kotlin)                        │
│   - Hash verification                                       │
│   - PackageInstaller session                                │
│   - Permission checks                                       │
│   - Emits: verifying/started/success/failed/cancelled       │
└─────────────────────────────────────────────────────────────┘

Acceptance Criteria

  • User can download, pause, resume, cancel downloads
  • User can install apps with proper verification
  • Multiple downloads queue correctly (max 3 concurrent)
  • Install failures show actionable error messages
  • Certificate mismatch offers force-update option
  • UI remains responsive throughout all operations
  • No operations block the UI thread

Files

  • lib/services/package_manager/package_manager.dart — Base class, state machine
  • lib/services/package_manager/install_operation.dart — State definitions
  • lib/services/package_manager/android_package_manager.dart — Android implementation
  • android/.../AndroidPackageManagerPlugin.kt — Native side