Gemini scanner sweep (token-bypass), GPT-4.1 cross-review, and the 6-detector /sh-security-review fan-out ran against 6acfdab..HEAD; every confirmed finding fixed: HIGH (deployment blockers, logic detector): - CONC-L1: suggestions lambda's bulk line-item PUT sent no proposalVersion — every AI suggestion job would 422 and be silently swallowed. Now fetches the proposal's rowVersion, echoes it, and retries once with a fresh token on 409. Pytest updated (38 green). - CONC-L2: mobile admin surface (update/approve/send/revise, bulk line items) sent no tokens — the entire mobile admin workflow would 422. Tokens threaded through mobile api layer + workspace/line-item screens with 409 refetch handling. tsc clean. MEDIUM-adjacent (scanner): - VendorProposalsController: the VendorTotalCost write on Proposal now bumps Version (was a silent lost-update path bypassing the guard). - FilesController: GeneratePDF audit staged into the same SaveChanges. LOW (detectors): - 409 envelope is schema-validated client-side (proposalConcurrencyConflictSchema.safeParse) and id-checked before seeding the react-query cache; malformed state degrades to invalidation (INJ-409-01/WEB-CONC-L1). - ProposalConcurrencyException.CurrentState typed ProposalResponse? so an EF entity can never serialize into the 409 body (SC-1). - Guard caller contract documented + GuardedEndpointAuthorizationTests reflection tripwire: guard-reaching endpoints must stay admin-gated (AUTHZ-CG-01). - Pre-check currentState now loads display navigations so both 409 paths return the same shape (CONC-L3). - Save chain's trailing getById failure no longer misreports a committed save; falls back to invalidation (CONC-L4). Also caught during fix verification: the handler's manual currentState serialization lacked JsonStringEnumConverter — enums would serialize as numbers, client schema validation would reject every guarded 409, and the state would always be discarded. Now matches the MVC pipeline and is pinned by a wire test. 193 xUnit / 70 vitest / 38 pytest green; mobile + shared tsc clean; Playwright smoke 2/2. |
||
|---|---|---|
| .. | ||
| fastlane | ||
| ios | ||
| patches | ||
| src | ||
| .env.example | ||
| .npmrc | ||
| app.json | ||
| babel.config.js | ||
| Gemfile | ||
| Gemfile.lock | ||
| index.js | ||
| metro.config.js | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Proposal System — Mobile (iOS)
React Native 0.85 iOS app for Sea Haven Industries field dispatchers. Submit proposals, capture vendor documents, and manage drafts with offline support.
Prerequisites
- Node.js 24+
- Ruby 3.x (for Fastlane)
- Xcode 26+ with iOS 26 SDK
- CocoaPods (installed via Bundler)
Local Development
# Install JS dependencies
npm install
# Install Ruby dependencies (Fastlane, CocoaPods)
bundle install
# Install native pods
cd ios && bundle exec pod install && cd ..
# Start Metro bundler
npm start
# Run on iOS simulator
npm run ios
Environment
The app reads configuration from src/config.ts. In development mode (__DEV__), the API URL points to http://localhost:5000/api. Run the .NET API locally or use the development proxy.
Authentication
Two login methods are supported:
- Email/Password — direct Cognito SRP auth via
amazon-cognito-identity-js - Google OAuth — Cognito Hosted UI PKCE flow via
react-native-app-auth
The iOS URL scheme com.seahavenind.proposals is registered in Info.plist for OAuth callbacks.
Code Signing
Certificates and provisioning profiles are managed by Fastlane Match using S3 storage:
- Bucket:
seahaven-ios-certificates(us-east-1) - Bundle ID:
com.seahavenind.proposals - Team ID:
9KAQYC653W
Match is configured in fastlane/Matchfile. The MATCH_PASSWORD secret decrypts signing assets.
CI/CD
The deploy-mobile.yaml workflow triggers on push to main (with mobile/** path filter) or manual workflow_dispatch. It calls the cd-mobile-ios.yaml reusable workflow which:
- Sets up
macos-26runner with Xcode 26 - Installs dependencies and pods
- Retrieves signing assets via Match (S3)
- Builds the IPA with Fastlane
- Uploads to TestFlight
Required GitHub Secrets
| Secret | Purpose |
|---|---|
AWS_DEPLOY_ROLE_ARN |
OIDC role for Match S3 access |
MATCH_PASSWORD |
Signing asset decryption passphrase |
ASC_KEY_ID |
App Store Connect API key ID |
ASC_ISSUER_ID |
App Store Connect API issuer |
ASC_KEY_CONTENT |
App Store Connect .p8 key (base64) |
Project Structure
mobile/
├── src/
│ ├── screens/ Auth, dispatcher, and admin screens
│ ├── lib/api/ API clients (auth, proposals, line items, admin)
│ ├── store/ Redux Toolkit (auth slice)
│ ├── navigation/ React Navigation (RootNavigator)
│ ├── components/ Reusable UI components
│ ├── hooks/ useAuth, useOfflineDraft, usePaginatedList
│ ├── theme/ Material Design 3 theming
│ ├── constants/ App-wide constants
│ └── config.ts Cognito + API configuration
├── ios/ Xcode project, assets, Info.plist
├── fastlane/ Fastfile, Matchfile, Appfile
├── Gemfile Ruby dependencies
└── package.json React Native 0.85.3