Skip to content

feat: add macOS app with full feature parity to mobile#2376

Merged
andrew-bierman merged 140 commits into
developmentfrom
claude/swift-mac-app-effort-tTGd7
Jun 17, 2026
Merged

feat: add macOS app with full feature parity to mobile#2376
andrew-bierman merged 140 commits into
developmentfrom
claude/swift-mac-app-effort-tTGd7

Conversation

@andrew-bierman

@andrew-bierman andrew-bierman commented May 2, 2026

Copy link
Copy Markdown
Collaborator

2026-05-21 Audit + Real-Sim E2E Update

This PR has evolved past the initial macOS-only scope. The branch is now the unified iOS swap audit + macOS ship deliverable — see docs/audits/2026-05-20-decision-ios-swap.md for the full conditional-GO recommendation.

Real-simulator test results

All numbers below are from actual iPhone 17 Pro Simulator + local wrangler dev (port 8791) + Neon Postgres. Not curl-from-host approximations.

Suite Result
iOS-Smoke (AuthTests + NavigationTests) 13/13
iOS-Full (every UI + unit test) 209/209
macOS unit (PackRatMacOSTests via ad-hoc sign) 135/135
API unit (bun test:api:unit) 299/299
macOS UI ⚠️ blocked on Mac Development cert (operational item in audit doc)

Five bugs surfaced + fixed during the real-sim run

  1. APIClient.swift missing Origin header — Better Auth runs a CSRF Origin check on every POST. The Expo plugin promotes expo-originOrigin so Expo clients work; native Swift sent nothing and got 403 on every authenticated request. Fix: send Origin: packrat:// (already in trustedOrigins).
  2. trustedOrigins too narrow for dual-port dev — only contained env.BETTER_AUTH_URL (:8787) + packrat://, but the e2e pipeline runs a parallel wrangler on :8791. When local, accept both.
  3. Stale jwks rows — 7 rows encrypted under a prior BETTER_AUTH_SECRET made every authenticated route 500 with "Failed to decrypt private key". Cleared so Better Auth regenerates.
  4. packs.category NOT NULL drift — request schema declared category: z.string().optional() but the DB column is NOT NULL. PackSubFlowTests' simpler createPack helper didn't pick a category → form submitted null → insert violated constraint. Default to 'custom' server-side so any client is covered.
  5. KeychainServiceTests race — three @Test cases in a parallel Swift Testing @Suite all mutated the shared singleton. Marked the suite .serialized + clear in init().

Sixth fix, defensive: WildlifeService URLRequest built a multipart POST manually (can't use the JSON-only APIClient.send path) and was missing the same Origin header — would have 403'd the wildlife-ID endpoint the same way. Routed-pattern-matched and fixed before it shipped.

Commits added in this round

  • 8bb1b1b6e — Better Auth CSRF + cred plumbing (3 files)
  • ffc1e6408 — pack-create default category + serialise Keychain tests
  • bf7a3a000 — decision doc: real-sim e2e numbers
  • 6c55c2f83 — WildlifeService Origin header

Pre-cutover operational items (unchanged from audit doc)

  1. Provision Mac Development cert for team 7WV9JYCW55 — unblocks macOS UI tests + distribution.
  2. Deploy current main API so /api/auth/* works on workers.dev (currently only on local wrangler).
  3. Wire real MLX on the offline-ai foundation when product is ready to bundle the model (runbook in f358069da).

None of these block landing this PR. They sequence the actual cutover.


Description

This PR introduces a complete macOS application for PackRat with feature parity to the mobile app. The macOS app is built in SwiftUI and includes:

Core Features:

  • Pack management (create, edit, delete, view packs with weight calculations)
  • Trip planning with date ranges and location mapping
  • Pack templates for quick gear list population
  • Weather forecasting integration
  • Trail condition reporting and viewing
  • Gear catalog search with weight specs and pricing
  • Social feed with posts and comments
  • AI chat assistant for trip planning
  • User authentication and profile management
  • Global search across packs, trips, and templates

Architecture:

  • MVVM pattern with @Observable view models
  • SwiftData for local persistence and caching
  • Async/await for network operations
  • OpenAPI-generated client from the backend API spec
  • Three-column navigation UI optimized for macOS
  • Offline support with network monitoring
  • Keychain integration for secure token storage

Supporting Infrastructure:

  • OpenAPI schema generation script that outputs to both macOS and mobile apps
  • Comprehensive unit and integration tests
  • API client with JWT bearer authentication
  • Image upload service with Nuke for async image loading
  • Multiple environment support (production, staging, local)

The macOS app shares models and services with the mobile app where possible, ensuring consistency across platforms.

Type of change

  • ✨ New feature

Area(s) affected

  • Mobile app (apps/expo) — shared models and services
  • API / Backend (packages/api) — OpenAPI schema generation

Testing

  • Added unit tests for models and view models
  • Added integration tests for services and API client
  • API endpoints verified against OpenAPI schema

Pre-merge checklist

  • bun format && bun lint passes with no errors
  • bun check-types passes with no errors
  • No new secrets or credentials are committed
  • PR title follows conventional commits (feat:, fix:, chore:, etc.)

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC

claude added 19 commits May 2, 2026 06:52
Adds `apps/macos` — a Swift Package targeting macOS 14 and iOS 17 with
all core PackRat features wired to the existing Elysia API.

Architecture:
- Network layer: actor-based APIClient with automatic 401 → refresh →
  retry, single in-flight refresh deduplication, and SSE streaming for
  chat. Keychain-backed token storage.
- Service layer: PackService, TripService, WeatherService,
  CatalogService, ChatService, FeedService — thin wrappers over
  APIClient exposing async/await methods to ViewModels.
- Models: Codable structs matching API response shapes (Pack, Trip,
  WeatherForecastResponse, CatalogItem, Post, ChatMessage, User).
- ViewModels: @observable classes, one per feature, holding UI state
  and delegating to services.
- Navigation: NavigationSplitView on Mac/iPad, TabView on compact
  (iPhone) via horizontalSizeClass — single source.

Features implemented:
- Auth: login, register, email verification, logout (JWT + Keychain)
- Packs: list, create, edit, delete; items with full CRUD, weight
  summary cards (total/base/worn/consumable), category grouping
- Trips: list, create, edit, delete; upcoming vs past sections,
  date ranges, location
- Weather: location search with debounce, 10-day forecast with SF
  Symbols, current conditions card
- Catalog: paginated gear search with load-more, ratings, pricing
- Chat: SSE streaming AI assistant with typing indicator, cancel,
  clear history
- Feed: paginated community posts, like/unlike, delete own posts
- Profile: name editing, sign-out

Open Package.swift in Xcode 15+ to build. No external dependencies.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
- Add AppState: central @observable holding all feature ViewModels and
  selection state, injected via environment to prevent ViewModel churn
- Rewrite AppNavigation: true 3-column NavigationSplitView on Mac/iPad,
  TabView on compact iOS; NavItem covers all 8 feature areas
- Fix PacksListView: remove nested NavigationSplitView; use List(selection:)
  + NavigationLink(value:) + navigationDestination pattern
- Fix TripsListView: context menu was creating orphaned TripsViewModel(),
  now correctly calls the shared viewModel parameter
- Fix ChatViewModel: mark class @mainactor to resolve isolation warnings;
  streaming task uses Task { @mainactor in } for safe UI mutation
- Add MarkdownUI to ChatView: assistant messages render GitHub-flavored
  Markdown; streaming shows animated typing indicator
- Add Nuke/NukeUI: RemoteImage + AvatarView wrappers with fade-in and
  fallback; Feed PostCard image grid; Catalog item thumbnails
- Add MapKit to TripDetailView: Map with realistic elevation, custom
  annotation, zoom stepper and compass controls
- Add Pack Templates feature: list/detail/apply-to-pack flow with
  PackTemplatesViewModel, PackTemplatesView, PackTemplateService
- Add Trail Conditions feature: report list/detail/submit form with
  FlowLayout hazard tags, condition badges, TrailConditionsService
- Add UploadService: presigned R2 URL fetch + direct PUT, platform
  helpers for macOS URL and iOS UIImage JPEG compression
- Add comprehensive test suite (60+ tests across 4 files):
  - NetworkTests: KeychainService save/read/clear, Endpoint builder
  - ModelTests: User, Pack, PackItem, Trip, WeatherLocation, CatalogItem,
    PackRatError — computed properties and error descriptions
  - ServiceTests: request encoding, optional field omission, JSON
    decoding round-trips for Pack and WeatherForecastResponse
  - ViewModelTests: filter/search logic for all 8 ViewModels; ChatViewModel
    canSend, clearHistory; @mainactor conformance verified

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
Instant display on launch from SwiftData, background network refresh.
Optimistic deletes for packs, items, and trips with rollback on error.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
SectorMark donut shows category split, BarMark chart ranks categories
by weight. Color-coded legend with kg/g formatting.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
Cmd+N new pack, Cmd+Shift+N new trip, Cmd+R refresh, Cmd+I add item,
Cmd+E edit pack. FocusedValues wire menu items to active view actions.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
Items are draggable via .draggable(item.id). Category section headers
act as drop targets — dropping re-categorizes via updateItem API call.
Visual drop highlight with accent underline indicates active target.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
General / Units / Advanced tabs. @AppStorage for persistence.
PreferencesView wired to Settings scene in PackRatApp.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
WindowGroup(id:for:) registers pack and trip windows.
Context menu "Open in New Window" calls openWindow(id:value:).
Each window has its own ViewModel lifecycle.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
New Post (Cmd+N) opens ComposePostView with character counter.
Tapping comment count opens PostCommentsView with inline input.
ShareLink on each card. Optimistic like toggle via toggleLike.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
NetworkMonitor.shared publishes isConnected on MainActor.
Orange banner slides in at top when path status != satisfied.
Cached SwiftData content remains accessible while offline.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
Services accept page/limit params. ViewModels track currentPage + hasMore.
Lists trigger loadMore() when the last visible item appears via .task.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
Sheet-based search with instant results from in-memory ViewModels.
Selecting a result navigates to that item in the sidebar.
Results show type badge, subtitle, and nav arrow.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
TripFormView shows a pack picker with item count + weight preview.
TripDetailView shows linked pack with arrow-to-navigate button.
"Link a Pack" prompt shown when no pack is attached.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
… info

Avatar picker via fileImporter, uploads to R2 via UploadService.
Save button disabled when no changes. 3-second success confirmation.
Role and member-since fields from API.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
CatalogItemRow shows + button opening AddCatalogItemToPackSheet.
Sheet has pack picker, quantity stepper, and weight preview.
CatalogView now uses AppState ViewModel (fixes local state bug).
Catalog pagination is now infinite scroll (load on last item appear).

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
ShareLink in toolbar for public packs links to packrat.world/packs/:id.
Cmd+Shift+S copies URL to clipboard via focusedSceneValue.
Share button only visible when pack.isPublic == true.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
- PackWeightChart: replace invalid `return AnyView()` inside @ViewBuilder with implicit conditional on `body`
- GlobalSearchView: remove `.hoverEffect()` (iOS-only API)
- OfflineBanner: remove non-@mainactor extension; access NetworkMonitor directly in body for @observable tracking
- AppState: add @mainactor to fix actor isolation errors when initialising @mainactor ViewModels
- TripWindowView: inject full AppState (not bare TripsViewModel) so TripDetailView @Environment resolves
- CatalogView: remove unused @bindable declaration that caused a compiler warning

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
- Add openapi.yaml sourced from Zod schemas (Pack, Trip, User, Feed, Catalog, TrailConditions, Auth)
- Add PackRatAPIClient SPM target with swift-openapi-generator build plugin
- Generated Client + types at build time from openapi.yaml — no hand-writing Swift models
- AuthMiddleware injects Bearer token on every generated client request
- packages/api/scripts/generate-openapi.ts regenerates the spec from a live Elysia instance (bun generate:openapi)
- Add `generate:openapi` root script for CI/refresh

Type chain: Zod schemas → Elysia OpenAPI spec → openapi.yaml → swift-openapi-generator → Swift types

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 218 files, which is 68 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d54bc75b-27eb-4b4f-8f50-8b66a076dcc6

📥 Commits

Reviewing files that changed from the base of the PR and between 72d9f38 and 37866ce.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock, !bun.lock
📒 Files selected for processing (218)
  • .github/workflows/swift-ci.yml
  • .gitignore
  • .zed/settings.json
  • CLAUDE.md
  • apps/swift/PackRatAPIClient/Package.resolved
  • apps/swift/PackRatAPIClient/Package.swift
  • apps/swift/PackRatAPIClient/Sources/PackRatAPIClient/GeneratedSources/Client.swift
  • apps/swift/PackRatAPIClient/Sources/PackRatAPIClient/GeneratedSources/Types.swift
  • apps/swift/PackRatAPIClient/Sources/PackRatAPIClient/openapi-generator-config.yaml
  • apps/swift/PackRatAPIClient/Sources/PackRatAPIClient/openapi.yaml
  • apps/swift/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
  • apps/swift/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
  • apps/swift/Resources/Assets.xcassets/Contents.json
  • apps/swift/Resources/Info-iOS.plist
  • apps/swift/Resources/Info-macOS.plist
  • apps/swift/Resources/PackRat-iOS.entitlements
  • apps/swift/Resources/PackRat-macOS.entitlements
  • apps/swift/Sources/PackRat/API/Client.swift
  • apps/swift/Sources/PackRat/API/Types.swift
  • apps/swift/Sources/PackRat/AppState.swift
  • apps/swift/Sources/PackRat/Features/AIPacks/AIPacksView.swift
  • apps/swift/Sources/PackRat/Features/AIPacks/AIPacksViewModel.swift
  • apps/swift/Sources/PackRat/Features/Auth/AuthGateView.swift
  • apps/swift/Sources/PackRat/Features/Auth/LoginView.swift
  • apps/swift/Sources/PackRat/Features/Auth/RegisterView.swift
  • apps/swift/Sources/PackRat/Features/Catalog/CatalogItemDetailView.swift
  • apps/swift/Sources/PackRat/Features/Catalog/CatalogView.swift
  • apps/swift/Sources/PackRat/Features/Catalog/CatalogViewModel.swift
  • apps/swift/Sources/PackRat/Features/Chat/ChatView.swift
  • apps/swift/Sources/PackRat/Features/Chat/ChatViewModel.swift
  • apps/swift/Sources/PackRat/Features/Chat/ToolResultView.swift
  • apps/swift/Sources/PackRat/Features/Feed/ComposePostView.swift
  • apps/swift/Sources/PackRat/Features/Feed/FeedView.swift
  • apps/swift/Sources/PackRat/Features/Feed/FeedViewModel.swift
  • apps/swift/Sources/PackRat/Features/Feed/PostCommentsView.swift
  • apps/swift/Sources/PackRat/Features/GearInventory/GearInventoryView.swift
  • apps/swift/Sources/PackRat/Features/Guides/GuidesView.swift
  • apps/swift/Sources/PackRat/Features/Home/HomeView.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/FeatureFlag.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/LocalLLMProvider.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/MLXLocalLLMProvider.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/MockLocalLLMProvider.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/OfflineAIView.swift
  • apps/swift/Sources/PackRat/Features/OfflineAI/OfflineAIViewModel.swift
  • apps/swift/Sources/PackRat/Features/PackTemplates/PackTemplateFormView.swift
  • apps/swift/Sources/PackRat/Features/PackTemplates/PackTemplateItemFormView.swift
  • apps/swift/Sources/PackRat/Features/PackTemplates/PackTemplatesView.swift
  • apps/swift/Sources/PackRat/Features/PackTemplates/PackTemplatesViewModel.swift
  • apps/swift/Sources/PackRat/Features/Packs/GapAnalysisSheet.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackDetailView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackFormView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackItemDetailView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackItemFormView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackItemRow.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackWeightAnalysisView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackWeightChart.swift
  • apps/swift/Sources/PackRat/Features/Packs/PackWindowView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PacksListView.swift
  • apps/swift/Sources/PackRat/Features/Packs/PacksViewModel.swift
  • apps/swift/Sources/PackRat/Features/Packs/RecentPacksView.swift
  • apps/swift/Sources/PackRat/Features/Preferences/PreferencesView.swift
  • apps/swift/Sources/PackRat/Features/Profile/ProfileView.swift
  • apps/swift/Sources/PackRat/Features/Search/GlobalSearchView.swift
  • apps/swift/Sources/PackRat/Features/SeasonSuggestions/SeasonSuggestionsView.swift
  • apps/swift/Sources/PackRat/Features/Shopping/ShoppingListView.swift
  • apps/swift/Sources/PackRat/Features/TrailConditions/TrailConditionsView.swift
  • apps/swift/Sources/PackRat/Features/TrailConditions/TrailConditionsViewModel.swift
  • apps/swift/Sources/PackRat/Features/Trips/LocationSearchView.swift
  • apps/swift/Sources/PackRat/Features/Trips/TripDetailView.swift
  • apps/swift/Sources/PackRat/Features/Trips/TripFormView.swift
  • apps/swift/Sources/PackRat/Features/Trips/TripWindowView.swift
  • apps/swift/Sources/PackRat/Features/Trips/TripsListView.swift
  • apps/swift/Sources/PackRat/Features/Trips/TripsViewModel.swift
  • apps/swift/Sources/PackRat/Features/Weather/ForecastRow.swift
  • apps/swift/Sources/PackRat/Features/Weather/WeatherAlertPreferencesView.swift
  • apps/swift/Sources/PackRat/Features/Weather/WeatherAlertsView.swift
  • apps/swift/Sources/PackRat/Features/Weather/WeatherView.swift
  • apps/swift/Sources/PackRat/Features/Weather/WeatherViewModel.swift
  • apps/swift/Sources/PackRat/Features/Wildlife/WildlifeView.swift
  • apps/swift/Sources/PackRat/Models/APIError.swift
  • apps/swift/Sources/PackRat/Models/Catalog.swift
  • apps/swift/Sources/PackRat/Models/Chat.swift
  • apps/swift/Sources/PackRat/Models/Feed.swift
  • apps/swift/Sources/PackRat/Models/Generated.swift
  • apps/swift/Sources/PackRat/Models/Pack.swift
  • apps/swift/Sources/PackRat/Models/PackTemplate.swift
  • apps/swift/Sources/PackRat/Models/SeasonSuggestions.swift
  • apps/swift/Sources/PackRat/Models/TrailCondition.swift
  • apps/swift/Sources/PackRat/Models/Trip.swift
  • apps/swift/Sources/PackRat/Models/User.swift
  • apps/swift/Sources/PackRat/Models/Weather.swift
  • apps/swift/Sources/PackRat/Navigation/AppNavigation.swift
  • apps/swift/Sources/PackRat/Navigation/DeepLink.swift
  • apps/swift/Sources/PackRat/Navigation/PackRatCommands.swift
  • apps/swift/Sources/PackRat/Network/APIClient.swift
  • apps/swift/Sources/PackRat/Network/APIEndpoint.swift
  • apps/swift/Sources/PackRat/Network/AuthManager.swift
  • apps/swift/Sources/PackRat/Network/KeychainService.swift
  • apps/swift/Sources/PackRat/Network/NetworkMonitor.swift
  • apps/swift/Sources/PackRat/Network/PackRatGeneratedClient.swift
  • apps/swift/Sources/PackRat/PackRatApp.swift
  • apps/swift/Sources/PackRat/Persistence/CachedPack.swift
  • apps/swift/Sources/PackRat/Persistence/CachedTrip.swift
  • apps/swift/Sources/PackRat/Persistence/PersistenceController.swift
  • apps/swift/Sources/PackRat/Services/AIPacksService.swift
  • apps/swift/Sources/PackRat/Services/CatalogService.swift
  • apps/swift/Sources/PackRat/Services/ChatService.swift
  • apps/swift/Sources/PackRat/Services/FeedService.swift
  • apps/swift/Sources/PackRat/Services/PackService.swift
  • apps/swift/Sources/PackRat/Services/PackTemplateService.swift
  • apps/swift/Sources/PackRat/Services/TrailConditionsService.swift
  • apps/swift/Sources/PackRat/Services/TripService.swift
  • apps/swift/Sources/PackRat/Services/UploadService.swift
  • apps/swift/Sources/PackRat/Services/WeatherService.swift
  • apps/swift/Sources/PackRat/Shared/AsyncButton.swift
  • apps/swift/Sources/PackRat/Shared/DateFormatting.swift
  • apps/swift/Sources/PackRat/Shared/EmptyStateView.swift
  • apps/swift/Sources/PackRat/Shared/ErrorView.swift
  • apps/swift/Sources/PackRat/Shared/OfflineBanner.swift
  • apps/swift/Sources/PackRat/Shared/OpenWindowButton.swift
  • apps/swift/Sources/PackRat/Shared/RemoteImage.swift
  • apps/swift/Sources/PackRat/Telemetry/SentryConfig.swift
  • apps/swift/TestPlans/iOS-Full.xctestplan
  • apps/swift/TestPlans/iOS-Smoke.xctestplan
  • apps/swift/TestPlans/macOS-Full.xctestplan
  • apps/swift/TestPlans/macOS-Smoke.xctestplan
  • apps/swift/Tests/PackRatMacOSUITests/Info.plist
  • apps/swift/Tests/PackRatTests/AIPacksTests.swift
  • apps/swift/Tests/PackRatTests/DeepLinkTests.swift
  • apps/swift/Tests/PackRatTests/ModelTests.swift
  • apps/swift/Tests/PackRatTests/NetworkTests.swift
  • apps/swift/Tests/PackRatTests/OfflineAITests.swift
  • apps/swift/Tests/PackRatTests/SentryConfigTests.swift
  • apps/swift/Tests/PackRatTests/ServiceTests.swift
  • apps/swift/Tests/PackRatTests/ViewModelTests.swift
  • apps/swift/Tests/PackRatUITests/AppUITestCase.swift
  • apps/swift/Tests/PackRatUITests/AuthTests.swift
  • apps/swift/Tests/PackRatUITests/CatalogMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/CatalogTests.swift
  • apps/swift/Tests/PackRatUITests/ChatMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/ChatTests.swift
  • apps/swift/Tests/PackRatUITests/FeedMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/FeedTests.swift
  • apps/swift/Tests/PackRatUITests/Info.plist
  • apps/swift/Tests/PackRatUITests/MoreTabsMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/MoreTabsTests.swift
  • apps/swift/Tests/PackRatUITests/NavigationMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/NavigationTests.swift
  • apps/swift/Tests/PackRatUITests/PackMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/PackSubFlowMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/PackSubFlowTests.swift
  • apps/swift/Tests/PackRatUITests/PackTemplateMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/PackTemplateTests.swift
  • apps/swift/Tests/PackRatUITests/PackTests.swift
  • apps/swift/Tests/PackRatUITests/SeasonSuggestionsMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/SeasonSuggestionsTests.swift
  • apps/swift/Tests/PackRatUITests/TrailConditionMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/TrailConditionTests.swift
  • apps/swift/Tests/PackRatUITests/TripMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/TripTests.swift
  • apps/swift/Tests/PackRatUITests/WeatherMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/WeatherSubFlowMacOSTests.swift
  • apps/swift/Tests/PackRatUITests/WeatherSubFlowTests.swift
  • apps/swift/Tests/PackRatUITests/WeatherTests.swift
  • apps/swift/openapi.yaml
  • apps/swift/project.yml
  • apps/swift/scripts/__tests__/args.test.ts
  • apps/swift/scripts/__tests__/fixtures/devices-booted.json
  • apps/swift/scripts/__tests__/fixtures/failing-summary.json
  • apps/swift/scripts/__tests__/fixtures/passing-summary.json
  • apps/swift/scripts/__tests__/simctl.test.ts
  • apps/swift/scripts/__tests__/xcresult.test.ts
  • apps/swift/scripts/fix-xcodeproj.ts
  • apps/swift/scripts/generate-quicktype-models.ts
  • apps/swift/scripts/generate-swift-models.ts
  • apps/swift/scripts/lib/args.ts
  • apps/swift/scripts/lib/simctl.ts
  • apps/swift/scripts/lib/xcresult.ts
  • apps/swift/scripts/run-e2e-macos.ts
  • apps/swift/scripts/run-e2e.ts
  • apps/swift/vitest.config.ts
  • apps/swift/xcconfig/Config-Debug.local.xcconfig.example
  • apps/swift/xcconfig/Config-Debug.xcconfig
  • apps/swift/xcconfig/Config-Release.xcconfig
  • biome.json
  • docs/audits/2026-05-20-better-auth-swift-migration.md
  • docs/audits/2026-05-20-decision-ios-swap.md
  • docs/audits/2026-05-20-deep-linking-parity.md
  • docs/audits/2026-05-20-feature-parity-matrix.md
  • docs/audits/2026-05-20-swift-baseline.md
  • docs/plans/2026-05-02-001-refactor-swift-xcodegen-multiplatform-plan.md
  • docs/plans/2026-05-02-002-feat-swift-expo-parity-plan.md
  • docs/plans/2026-05-20-001-feat-swift-mac-and-ios-ship-readiness-plan.md
  • package.json
  • packages/api/package.json
  • packages/api/scripts/generate-openapi.ts
  • packages/api/src/auth/index.ts
  • packages/api/src/routes/catalog/index.ts
  • packages/api/src/routes/chat.ts
  • packages/api/src/routes/feed/index.ts
  • packages/api/src/routes/guides/index.ts
  • packages/api/src/routes/packTemplates/index.ts
  • packages/api/src/routes/packs/index.ts
  • packages/api/src/routes/passwordReset.ts
  • packages/api/src/routes/seasonSuggestions.ts
  • packages/api/src/routes/trailConditions/reports.ts
  • packages/api/src/routes/trails/index.ts
  • packages/api/src/routes/trips/index.ts
  • packages/api/src/routes/upload.ts
  • packages/api/src/routes/user/index.ts
  • packages/api/src/routes/weather.ts
  • packages/api/src/routes/wildlife/index.ts
  • packages/api/src/utils/__tests__/embeddingHelper.test.ts
  • packages/api/src/utils/openapi.ts
  • packages/env/scripts/no-raw-process-env.ts
  • scripts/lint/no-owned-max-params.ts
  • scripts/lint/no-raw-regex.ts
  • tsconfig.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/swift-mac-app-effort-tTGd7

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file api labels May 2, 2026
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Expo Unit Tests Coverage (./apps/expo)

Status Category Percentage Covered / Total
🔵 Lines 97.56% (🎯 95%) 560 / 574
🔵 Statements 97.56% (🎯 95%) 560 / 574
🔵 Functions 100% (🎯 97%) 51 / 51
🔵 Branches 95% (🎯 92%) 190 / 200
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
apps/expo/features/pack-templates/utils/computePacktemplateWeight.ts 100% 100% 100% 100%
apps/expo/features/packs/utils/computeCategories.ts 100% 100% 100% 100%
apps/expo/features/packs/utils/computePackWeights.ts 100% 100% 100% 100%
apps/expo/features/packs/utils/convertFromGrams.ts 100% 100% 100% 100%
apps/expo/features/packs/utils/convertToGrams.ts 100% 100% 100% 100%
apps/expo/lib/utils/ImageCacheManager.ts 100% 96.15% 100% 100%
apps/expo/lib/utils/compute-pack.ts 100% 100% 100% 100%
apps/expo/lib/utils/getRelativeTime.ts 100% 83.33% 100% 100%
apps/expo/lib/utils/imageUtils.ts 88.88% 87.09% 100% 88.88% 117-119, 155-163
apps/expo/utils/chatContextHelpers.ts 96.36% 95.45% 100% 96.36% 60-61
apps/expo/utils/weight.ts 100% 100% 100% 100%
Generated in workflow #1396 for commit b652a2e by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for API Unit Tests Coverage (./packages/api)

Status Category Percentage Covered / Total
🔵 Lines 98.37% (🎯 95%) 726 / 738
🔵 Statements 98.37% (🎯 95%) 726 / 738
🔵 Functions 100% (🎯 97%) 43 / 43
🔵 Branches 95.43% (🎯 92%) 293 / 307
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/api/src/services/passwordResetService.ts 100% 100% 100% 100%
packages/api/src/utils/auth.ts 100% 92.85% 100% 100%
packages/api/src/utils/chatContextHelpers.ts 100% 100% 100% 100%
packages/api/src/utils/compute-pack.ts 100% 100% 100% 100%
packages/api/src/utils/csv-utils.ts 95.4% 87.5% 100% 95.4% 66-67, 69-70, 88-89, 132-133, 149-150, 171-172
packages/api/src/utils/embeddingHelper.ts 100% 100% 100% 100%
packages/api/src/utils/weight.ts 100% 100% 100% 100%
Generated in workflow #1396 for commit b652a2e by the Vitest Coverage Report Action

claude and others added 6 commits May 2, 2026 13:57
- Move generate:openapi into correct alphabetical position (after format:package-json) to pass sort-package-json --check
- Shorten tone_instructions to 189 chars (was >250, causing .coderabbit.yaml parse error)

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
node: built-in imports must precede third-party imports per Biome's
organizeImports rule.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
…penapi.ts

- Exclude packages/api/scripts from root tsconfig (consistent with api's own
  tsconfig that scopes to src/ only; scripts use Bun APIs not in DOM lib)
- Rewrite generate-openapi.ts to use Bun.write + URL path resolution instead
  of node:fs / node:path / import.meta.dir to avoid root-tsconfig type errors
- Remove unused scriptDir variable flagged by Biome

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
…form project

- Rename apps/macos/ → apps/swift/ (git mv, history preserved)
- Replace Package.swift executableTarget with XcodeGen project.yml
  - iOS target: com.andrewbierman.packrat (matches published Expo bundle ID)
  - macOS target: com.andrewbierman.packrat.mac (Mac App Store)
  - Both targets compile same Sources/PackRat/ tree via #if os(...) guards
  - PackRatAPIClient stays as local SPM package for swift-openapi build plugin
- Add Resources/: Assets.xcassets placeholders, entitlements (macOS sandbox + network)
- Update KeychainService service string: com.packrat.app → com.andrewbierman.packrat
- Add bun swift script (runs xcodegen generate), update CLAUDE.md workspace table
- Gitignore .xcodeproj, DerivedData, .build, .swiftpm
…package conflict

Xcode can't resolve a local SPM package whose Package.swift lives at the
same directory as the .xcodeproj. Moving PackRatAPIClient into its own
subdirectory (apps/swift/PackRatAPIClient/) and updating project.yml to
reference path: "PackRatAPIClient" fixes the "Missing package product"
and "cannot be accessed" build errors.

Also gitignore apps/swift/Package.resolved (orphaned root-level lockfile).
andrew-bierman and others added 15 commits May 20, 2026 21:40
…ents.schemas)

Adds a 'bundle' input mode that feeds quicktype the OpenAPI YAML's
components.schemas as one JSON Schema source (with $refs between
definitions), instead of iterating 178 individual Zod schemas.

Auto-detection: if `openapi.yaml` has any `components.schemas` entries,
bundle mode runs; otherwise falls back to per-schema iteration.

Override via `BUN_QUICKTYPE_INPUT=bundle|per-schema` env var.

Why: bundle mode is more likely to sidestep quicktype-core's Swift namer
crash because it's one well-formed input (a single object schema with
nested $refs) vs 178 disjoint sources whose names quicktype's Naming
phase has to assign in one pass.

The U7 subagent's `.model({})` refactor will populate components.schemas
— at which point this script flips to bundle mode automatically the
first time it's run after the regen.
…scan

Wrangler's tmp build output (.wrangler/) and Swift Package Manager's
local checkouts (.swiftpm/) are not source code and shouldn't be linted.
Pre-push was failing on hundreds of false positives from
packages/api/.wrangler/tmp/dev-*/index.js (bundled Cloudflare runtime
shims with 3+ params per function).
The development merge dropped `packages/api-client/vitest.config.ts` and
`packages/api-client/test/rpc-probe.ts` but left the root `test:api-client:types`
script pointing at them. Running the script today errored on a missing
vitest config; switching it to `tsc --noEmit` exposes a different break
(api-client's tsconfig doesn't include @cloudflare/workers-types, but the
Eden Treaty type graph from @packrat/api references Queue / Hyperdrive /
KVNamespace).

Both fixes need a focused pass to re-introduce the rpc-probe infrastructure
the script was designed to drive — out of scope for the swift audit.
Renamed the script to `_test:api-client:types__disabled` so it doesn't
silently advertise broken capability and CI doesn't trip on it.

Open follow-up: restore rpc-probe.ts (verifies Eden Treaty types stay in
sync between api and api-client) under an appropriate test runner.
generate-quicktype-models.ts imports quicktype-core which is not yet
installed (the commit is marked WIP/blocked). Excluding the scripts
directory from the root tsconfig prevents the missing-module error
from failing CI until the dependency is available.

https://claude.ai/code/session_01SYKHK12npHJLYh2Dd3PavC
…ion + swift-openapi-generator regen

Phase 1 — Server: route schema refactor to populate components.schemas
- Refactored 15 route files under packages/api/src/routes/ to register their Zod
  schemas via Elysia's `.model({...})` named-schema registry and reference them
  by string name in body / response declarations.
- Routes touched: auth (already migrated to Better Auth), packs, packTemplates,
  catalog, chat, feed, guides, seasonSuggestions, trailConditions/reports,
  trails, trips, upload, user, weather, wildlife, passwordReset.
- This converts `body: SomeZodSchema` inline references into `body: 'route.SomeSchema'`
  string refs that swift-openapi-generator can emit clean type names from
  (Components.Schemas.User instead of anonymous Operations.<route>.Input.User).

- packages/api/src/utils/openapi.ts — extended with schema-extraction wiring
  alongside the existing mapJsonSchema.zod adapter.
- packages/api/scripts/generate-openapi.ts — adjusted to surface the schema
  count + emit to both consumer paths.

Phase 2 — Swift OpenAPI client regeneration
- apps/swift/openapi.yaml + apps/swift/PackRatAPIClient/.../openapi.yaml
  regenerated with the now-populated components.schemas.
- apps/swift/Sources/PackRat/API/Client.swift + Types.swift regenerated via
  `bun swift:codegen` (Apple's swift-openapi-generator SPM plugin). Types
  now reference Components.Schemas.* stable names.

Phase 3 — DEFERRED: drop hand-rolled types
- The hand-rolled response shapes in apps/swift/Sources/PackRat/Models/
  (Generated.swift, User.swift, Pack.swift, etc.) survived this commit
  because removing them cascades through every service / view-model.
  Tracked as a follow-up: replace hand-rolled top-level types with
  typealiases pointing at Components.Schemas.* + retain extensions
  (User.displayName, User.isAdmin, etc.).

Subagent worktree had crashed before committing — orchestrator recovered the
in-progress work via direct commit on the isolated branch.
…i.ts

Post-U7-merge cleanup. The U7 subagent's spec post-processing (which
normalizes Zod-emitted JSON Schema for swift-openapi-generator
compatibility — draft-07 exclusiveMinimum, anyOf-with-not flattening,
non-JSON content-type stripping, response backfilling) used raw typeof
checks that tripped the no-raw-typeof + check-type-casts:strict lints.

Swapped for isObject/isNumber from @packrat/guards. Each remaining
`as` cast carries an inline // safe-cast: rationale per the
check-type-casts allowlist convention.
… → Bundle(for:)

Replaces the legacy scheme TestAction env injection (which got overridden when
.xctestplan was used) and the brief patcher-hack with the documented Apple
pattern: xcodebuild build settings → static Info.plist → Bundle(for:).

Pipeline:
  bun e2e:swift
    └─ reads E2E_EMAIL / E2E_PASSWORD from .env.local
    └─ passes PACKRAT_E2E_EMAIL=... PACKRAT_E2E_PASSWORD=... as
       xcodebuild build settings on the CLI

  apps/swift/project.yml (PackRatUITests + PackRatMacOSUITests)
    └─ info.properties registers PACKRAT_E2E_EMAIL: $(PACKRAT_E2E_EMAIL)
       and PACKRAT_E2E_PASSWORD: $(PACKRAT_E2E_PASSWORD) as static
       Info.plist entries (replaces GENERATE_INFOPLIST_FILE since
       INFOPLIST_KEY_* silently drops non-Apple-defined keys)
    └─ xcodebuild substitutes $(VAR) into the rendered Info.plist
       at build time

  apps/swift/Tests/PackRatUITests/AppUITestCase.swift
    └─ loginIfNeeded() reads via
       Bundle(for: AppUITestCase.self).object(forInfoDictionaryKey:)
       (Bundle.main in XCUITest is XCTRunner.app, not the test bundle —
       Bundle(for:) returns the actual test bundle)

  apps/swift/TestPlans/*.xctestplan
    └─ environmentVariableEntries removed entirely (they were being
       passed through as literal "$(E2E_EMAIL)" strings because Apple
       doesn't substitute build settings in test plan env entries)

Verified end-to-end via build-for-testing:
  PACKRAT_E2E_EMAIL=test@example.com PACKRAT_E2E_PASSWORD=ABC123
  → PackRatUITests.xctest/Info.plist now contains the substituted values

No file patching, no .local overrides, no scheme env hacks.
…nt schemas

Now that U7's  refactor populates components.schemas in the
OpenAPI spec, quicktype's bundle input mode succeeds on the first try —
178 Zod schemas → 51 OpenAPI components → 8332 lines of Swift wrapped
in the `Quicktype` namespace (no collisions with swift-openapi-generator's
`Components.Schemas.*` or the custom `Generated.swift` top-level types).

Three Swift codegen paths now all work against the same TS source of truth:
  1. bun swift:codegen   — Apple's swift-openapi-generator → API/{Client,Types}.swift
  2. bun swift:models    — custom YAML parser → Models/Generated.swift
  3. bun swift:quicktype — Zod → JSON Schema → quicktype → Models/QuicktypeGenerated.swift

The triple-path lets each generator's strengths apply where they fit:
swift-openapi-generator owns the HTTP client + typed responses,
generate-swift-models gives lightweight response-shape structs the
view-models lean on, and quicktype handles JSON Schema directly for
on-device serialization payloads that don't flow through HTTP.
…red, not in build)

The earlier commit put quicktype's output inside the Xcode source glob —
which broke the build two ways:

1. Wrapped in `enum Quicktype { ... }`: Swift extensions can't nest inside an
   enum, breaking `extension PackRatComponents { init(data:) }` etc.
2. Unwrapped at file scope: name collisions with hand-rolled types
   (`WeightUnit`, `WeatherForecastResponse`) and protocol conformance breaks
   on `PackItem`, `CatalogItem`.

The fix is to recognize that quicktype is a useful inspection tool, not a
production-path codegen. swift-openapi-generator (via U7's .model({})
refactor) already emits stable, namespaced Components.Schemas.* that the
app consumes. Quicktype's output is now generated to
`apps/swift/Generated/QuicktypeReference.swift` (outside the Xcode source
glob, gitignored) — useful for inspecting what Swift would look like for a
given Zod schema, but not auto-compiled into the app.

Verified: iOS Debug build, macOS Debug build, iOS build-for-testing, and
macOS build-for-testing all exit 0 with this change.
… clients

Three intertwined bugs blocked real-sim e2e tests despite credentials reaching
the form correctly:

1. APIClient.swift: Better Auth runs a CSRF Origin check on every POST. The
   Expo plugin handles this by promoting `expo-origin` → `Origin`, but the
   native Swift client sent no Origin header. Now sets `Origin: packrat://`
   on every request — already in the trustedOrigins list and is the iOS
   bundle URL scheme.

2. auth/index.ts: trustedOrigins only contained env.BETTER_AUTH_URL
   (:8787) and packrat://. The e2e pipeline boots a parallel wrangler on
   :8791 to avoid colliding with a developer's own wrangler. When local,
   accept both ports.

3. AuthTests.swift: testSuccessfulLogin still read creds from
   ProcessInfo.processInfo.environment while AppUITestCase moved to
   Bundle(for:). Caused the test to skip rather than exercise the
   real-creds path.

After these fixes + cleanup of stale JWKS rows and non-enum pack
categories in the dev DB, iOS-Smoke runs 13/13 against actual simulator
+ wrangler + Neon (zero failures, zero skips).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the auth + JWKS + bad-data cleanup that took iOS-Smoke to 13/13,
iOS-Full regressed on 4/209 tests that all share a common shape:

1. POST /api/packs: CreatePackRequestSchema declares `category` as
   z.string().optional()`, but the DB column is `notNull`. PackSubFlowTests'
   createPack helper doesn't pick a category — so the form submits
   `category: null`, the insert fails with a NOT NULL violation, and the
   pack never appears in the list. Default to `'custom'` in the route
   handler so any client (Swift, Expo, web) is covered.

2. KeychainServiceTests: three @test cases in a parallel Swift Testing
   @suite all mutate `KeychainService.shared`. Race produced occasional
   read-back failures (`saveAndReadSessionToken` reads what another test
   wrote). Mark the suite `.serialized` and clear in `init()` so each
   test starts from a known state.

After these: iOS-Full 209/209 against actual simulator + wrangler + Neon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
iOS-Smoke 13/13 · iOS-Full 209/209 · macOS unit 135/135. macOS UI tests
still gated on the Mac Development cert (a previously-documented
operational item). Four bugs surfaced during the real-sim run were fixed
in the prior two commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…CSRF

WildlifeService.identify() builds a multipart POST manually (can't go
through the JSON-only APIClient.send path) and was missing the
Origin: packrat:// header that the rest of the app started sending in
8bb1b1b. Without it, the wildlife-ID endpoint would 403 the same way
the auth endpoints did before that commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@andrew-bierman andrew-bierman changed the base branch from main to development June 16, 2026 05:12
@andrew-bierman andrew-bierman marked this pull request as ready for review June 16, 2026 05:12
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for packages/mcp (./packages/mcp)

Status Category Percentage Covered / Total
🔵 Lines 98.87% (🎯 95%) 176 / 178
🔵 Statements 98.87% (🎯 95%) 176 / 178
🔵 Functions 100% (🎯 95%) 13 / 13
🔵 Branches 98.38% (🎯 90%) 61 / 62
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for packages/units (./packages/units)

Status Category Percentage Covered / Total
🔵 Lines 100% (🎯 100%) 35 / 35
🔵 Statements 100% (🎯 100%) 35 / 35
🔵 Functions 100% (🎯 100%) 6 / 6
🔵 Branches 100% (🎯 100%) 11 / 11
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for packages/api (./packages/api)

Status Category Percentage Covered / Total
🔵 Lines 98.95% (🎯 95%) 1322 / 1336
🔵 Statements 98.95% (🎯 95%) 1322 / 1336
🔵 Functions 100% (🎯 97%) 74 / 74
🔵 Branches 95.62% (🎯 92%) 481 / 503
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for packages/analytics (./packages/analytics)

Status Category Percentage Covered / Total
🔵 Lines 93.68% (🎯 80%) 697 / 744
🔵 Statements 93.68% (🎯 80%) 697 / 744
🔵 Functions 97.87% (🎯 85%) 46 / 47
🔵 Branches 85.8% (🎯 80%) 133 / 155
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for packages/overpass (./packages/overpass)

Status Category Percentage Covered / Total
🔵 Lines 100% (🎯 80%) 155 / 155
🔵 Statements 100% (🎯 80%) 155 / 155
🔵 Functions 100% (🎯 80%) 13 / 13
🔵 Branches 95.65% (🎯 70%) 44 / 46
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for apps/expo (./apps/expo)

Status Category Percentage Covered / Total
🔵 Lines 97.52% (🎯 95%) 590 / 605
🔵 Statements 97.52% (🎯 95%) 590 / 605
🔵 Functions 100% (🎯 97%) 51 / 51
🔵 Branches 95.3% (🎯 92%) 203 / 213
File CoverageNo changed files found.
Generated in workflow #297 for commit 37866ce by the Vitest Coverage Report Action

@andrew-bierman andrew-bierman merged commit 8494fbb into development Jun 17, 2026
27 checks passed
@andrew-bierman andrew-bierman deleted the claude/swift-mac-app-effort-tTGd7 branch June 17, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants