Building an AI-Native iOS Budgeting App in Two Weeks

Architecture and implementation lessons from an AI-assisted iOS budgeting app: Flutter, Firebase, Gmail events, category suggestions, precise money handling and review.

Building an AI-Native iOS Budgeting App in Two Weeks — Artificial Intelligence

This article traces a two-week AI-assisted development effort for an iOS budgeting app. It focuses on the architecture and implementation lessons; a short development timeline does not establish production readiness.

The design connects Gmail change notifications, transaction parsing, Claude category suggestions, Firestore and mobile notifications. The examples below are instructional sketches and omit application-specific implementation; deployment, security and financial correctness require separate validation.

This isn't about hype. It's about what's actually possible in 2025 when you combine modern serverless infrastructure, AI-native development tools, and intelligent product design. The future of software development isn't just AI-assisted—it's AI-native, from the code we write to the features we ship.

FinHub is the budgeting-app example used here to explore that design.

The Vision: What Makes FinHub Different

Most budgeting apps suck. They require manual transaction entry, don't sync with all your banks, use clunky categorization rules, and feel like homework. The product goal was software that just worked—automatically, intelligently, instantly.

The product requirements:

  • Manual expense tracking is tedious and error-prone
  • Traditional budgeting apps require constant data entry
  • Bank apps don't provide multi-account aggregation
  • Generic categorization doesn't match user spending patterns
  • International banking isn't well-supported

The AI-native solution I built:

Transaction extraction: an authorized mailbox event can trigger parsing and a proposed transaction. Unsupported formats and ambiguous amounts need a visible review path.

Category suggestions: Claude receives selected transaction context and prior corrections. This is contextual reuse, not evidence that the underlying model is retrained.

Asynchronous processing: Gmail notifications can trigger Cloud Functions after mailbox changes. End-to-end latency varies across the bank, Gmail, Pub/Sub, model, database and device.

Multi-currency support: the example uses COP and USD with exchange rates refreshed daily. Store the rate, source and effective time used for each conversion.

Correction reuse: reviewed category changes can become examples for future prompts. Measure whether they improve categorization; each correction does not guarantee a better result.

Dark Cyberpunk UI: Minimalist, fast, focused.

The design uses AI for development and category suggestions, with serverless components for hosting. Error handling, credential isolation, correct arithmetic, tested rules and recovery behavior remain explicit engineering responsibilities.

The Tech Stack: Architecture Deep Dive

Frontend: Flutter + Dart

Framework: Flutter 3.32.7
Language: Dart 3.8.1  
Platform: iOS (iPhone)
UI: Material Design 3 with custom dark theme
State Management: Provider + Firestore streams
Navigation: go_router with auth guards

Why Flutter? Single codebase for beautiful native iOS UI. Hot reload means instant feedback during development. Excellent Firebase integration out of the box. Strong type safety with Dart catches bugs at compile time. Native performance without the complexity of Swift/SwiftUI.

Backend: Firebase Serverless Architecture

Authentication: Google Sign-In (OAuth 2.0, Gmail API scopes)
Database: Cloud Firestore (real-time NoSQL)
Compute: Cloud Functions (Node.js 20, TypeScript)
Messaging: Firebase Cloud Messaging + APNS

Firestore Collections:

  1. users — client-readable profile fields only, with explicitly authorized access.
  2. serverCredentials — OAuth tokens stored separately and denied to client reads; backend access restricted by IAM.
  3. pendingCandidates — deduplicated extracted proposals and review state; ingestion does not change balances. transactions — confirmed monetary records with currency, integer minor units, provenance and a stable posting key.
  4. wallets — account records and balances using the same money representation.
  5. categories — available classification choices.
  6. keywordMappings — reviewed examples used as context, not model training.
  7. emailLogs — processing audit records with an appropriate retention policy.
  8. notifications — delivery intents and in-app notification history.

Access design to implement and verify:

  • Enforce user-scoped access to client data and protect balance updates.
  • Keep OAuth credentials in server-only storage separate from readable profile documents. Firestore rules cannot hide individual fields in a readable document.
  • Test client rules and backend IAM separately; server SDK authorization is a separate boundary.

Composite Indexes:

  • transactions(userId, timestamp DESC) - Main transaction list
  • transactions(userId, category, timestamp DESC) - Category filtering
  • transactions(userId, walletId, timestamp DESC) - Wallet filtering

Cloud Functions Architecture (TypeScript)

functions/
├── src/
│   ├── index.ts                    # Function entry points
│   ├── ai/
│   │   └── claude-service.ts       # Claude AI integration
│   ├── gmail/
│   │   ├── auth-service.ts         # OAuth token management
│   │   ├── gmail-service.ts        # Gmail API client
│   │   └── watch-service.ts        # Pub/Sub watch setup
│   ├── transactions/
│   │   └── transaction-builder.ts  # Email → Transaction parser
│   └── notifications/
│       ├── push-notification-service.ts  # FCM
│       └── in-app-notification-service.ts # Firestore

Key Cloud Functions:

processGmailNotification (Pub/Sub trigger)

  • Triggered by Gmail Pub/Sub watch webhook
  • Fetches new emails from Gmail API
  • Identifies bank source (Bank A/Bank B/Payment Provider)
  • Parses transaction details (amount, merchant, date)
  • Calls Claude AI for categorization
  • Durably stores a deduplicated pending candidate and a review-notification outbox entry; no ledger or balance update yet.
  • Sends push notification to iPhone

triggerTokenRefresh (Scheduled, hourly)

  • Ensures Gmail API access tokens stay fresh
  • Rotates OAuth tokens automatically
  • Re-establishes Pub/Sub watch if expired

updateExchangeRate (Scheduled, daily)

  • Fetches COP/USD exchange rate
  • Updates Firestore for multi-currency calculations
  • Sends notification if rate changes significantly

storeAccessToken (Callable)

  • Called from Flutter app after Google Sign-In
  • Stores OAuth credentials in server-only storage after validating the authenticated user and authorization result.
  • Sets up Gmail Pub/Sub watch

AI Integration: Claude Console API

interface ClaudeResponse {
  type: "TRANSACTIONAL" | "INFORMATIONAL" | "UNKNOWN";
  category?: string;                  // Emoji (🏠, 🥩, 🚕)
  categoryDescription?: string;       // "Rent", "Groceries", "Transport"
  merchant?: string;
  amountDecimal?: string;           // Exact decimal text, e.g. "10.25"
  currency?: string;                // Validated currency code, e.g. "USD"
  confidence?: number;                // 0-100
  message?: string;
}

// Model output is untrusted. Validate decimal syntax, currency and range.
// Parse exactly into currency-specific integer minor units; reject excess
// precision instead of converting through a binary floating-point number.

The AI Learning Loop:

  1. Email arrives → Claude analyzes content
  2. AI categorizes based on:
    • Last 20 transactions (context)
    • Last 50 user corrections (learning data)
    • All available categories
    • Email sender (Bank A/Bank B/Payment Provider)
  3. User reviews categorization
  4. If user changes category → stored in keywordMappings
  5. Future prompts may reuse reviewed corrections; evaluate accuracy rather than assuming improvement.

Why evaluate a model for categorization:

  • Compare a model with rules on representative transaction descriptions.
  • Use selected reviewed examples as prompt context.
  • Evaluate supported languages and changed email formats.
  • Validate structured responses and amounts before writing records.
  • Estimate cost from workload, model usage and current public provider prices; no private account spending is reported.

The Real Innovation: Gmail Pub/Sub Architecture

Push notifications can avoid repeated empty mailbox polls. They signal that changes are available; reliable synchronization still requires saved cursors, recovery and periodic reconciliation of missed notifications.

The Pub/Sub solution:

graph LR
    A[Bank Email Sent] --> B[Gmail Receives]
    B --> C[Gmail Pub/Sub Watch]
    C --> D[Cloud Pub/Sub Topic]
    D --> E[processGmailNotification]
    E --> F[Gmail API Fetch]
    F --> G[Claude AI Categorization]
    G --> H[Persist Pending Candidate and Outbox]
    H --> I[FCM Push Notification]
    I --> J[iPhone Lock Screen]

    style A fill:#f7768e
    style C fill:#7dcfff
    style E fill:#e0af68
    style G fill:#bb9af7
    style I fill:#7aa2f7
    style J fill:#bb9af7

How Gmail Pub/Sub Watch Works:

1. Setup Phase (after user signs in):

await gmail.users.watch({
  userId: 'me',
  requestBody: {
    topicName: 'projects/example-project/topics/gmail-notifications',
    labelIds: ['INBOX']
  }
});

2. Email Arrives:

  • An authorized mailbox receives a transaction-related email.
  • Gmail publishes a change signal to Pub/Sub.
  • The worker synchronizes history asynchronously, with retries and missed-event recovery.

3. Cloud Function Triggered:

export const processGmailNotification = onMessagePublished(
  { topic: "gmail-notifications" },
  async (event) => {
    const historyId = event.data.message.json.historyId;
    // Fetch new emails since last history ID
    // Process each email...
  }
);

4. Email Processing Pipeline:

  • Identify sender (Bank A/Bank B/Payment Provider filters)
  • Extract transaction data (regex parsing + Claude AI)
  • Determine wallet (based on email source)
  • Persist a deduplicated pending candidate and review-notification outbox atomically.
  • Leave the ledger and wallet balance unchanged until explicit confirmation.
  • Deliver the review notification from the outbox; on confirmation, post the transaction and balance change once.

5. Push Notification to iPhone:

// Illustrative visible alert; no financial details on the lock screen.
await admin.messaging().send({
  token: userFCMToken,
  notification: {
    title: "Transaction ready for review",
    body: "Open the app to review the details."
  },
  apns: {
    headers: { "apns-push-type": "alert", "apns-priority": "10" },
    payload: { aps: { sound: "default", badge: 1 } }
  }
});

Token Management Challenge:

  • Gmail Pub/Sub watch expires after 7 days
  • OAuth access tokens expire after ~1 hour
  • Solution: Scheduled Cloud Function runs hourly to refresh

Operational behavior to measure:

  • Measure end-to-end latency and its upper percentiles.
  • Renew Gmail watches and reconcile missed notifications.
  • Handle retries and duplicate deliveries idempotently.
  • Treat notification display and background execution as conditional device behavior.

The AI Development Process: Building with Claude Code

This is where things get meta. I used AI to build an AI-powered app.

The AI Trifecta:

  1. Claude Code - Primary development AI (autonomous coding)
  2. Cursor - AI-powered code editor (context-aware completions)
  3. Cursor Bug Bot - Automated PR review (catches bugs before merge)

How Claude Code Accelerated Development

Day 1-2: Project Setup & Architecture

Me: "Start mvp"

Claude Code:
  - Loads PRD from .claude/prd/mvp/
  - Presents 8-phase development plan
  - Asks for confirmation

Me: "Proceed"

Claude Code:
  - Creates Flutter project
  - Configures Firebase
  - Sets up folder structure
  - Deploys security rules
  - Commits + pushes to GitHub
Illustrative conversation/workflow, not built-in shell commands.

No boilerplate hell. No "let me Google how to set up Firebase." Just describe what you want, confirm the plan, and Claude Code executes.

Day 3-5: Data Models & Backend Services

  • Claude Code wrote 7 Firestore data models with JSON serialization
  • Generated CRUD services for each collection
  • Created default data initialization logic
  • All with proper error handling, logging, and type safety
  • Human input: Reviewed architecture decisions, adjusted business logic

Day 6-8: Gmail API Integration This was the most complex part: OAuth flows, token management, Pub/Sub webhooks.

Claude Code:

  • Researched Gmail API documentation
  • Implemented OAuth 2.0 flow with refresh token handling
  • Set up Pub/Sub watch with automatic renewal
  • Created email parsing logic for Bank A/Bank B/Payment Provider
  • Built transaction extraction pipeline

Human oversight: Review representative authorized fixtures, refine parsing and test edge cases.

Day 9-10: Claude AI Integration Claude Code integrated its own API (yes, the AI coded itself into the product).

  • Designed learning loop architecture
  • Implemented category suggestion logic
  • Built feedback collection system
  • Human decision: Evaluate the selected model against alternative approaches on the intended workload.

Day 11-12: UI/UX Implementation

  • Dark cyberpunk theme with custom color palette
  • Transaction list with real-time Firestore streams
  • Wallet management screens
  • Category selection with emoji support
  • Reports and analytics
  • Human touch: Final design tweaks, animation timing

Day 13-14: Testing, Bug Fixes, Push Notifications

  • Tests for documented acceptance criteria.
  • Fixed edge cases (floating-point precision, multi-currency)
  • Implemented FCM + APNS for background notifications
  • Deployment requires its own operational acceptance checks.

The Claude Code Workflow Pattern

# 1. Start a session
start mvp

# 2. Claude Code:
#    - Pulls latest from GitHub
#    - Loads PRD context
#    - Identifies next task
#    - Presents plan
#    - WAITS for confirmation

# 3. Developer confirms or adds requirements
proceed  # or "add error handling" or "use different approach"

# 4. Claude Code:
#    - Implements the feature
#    - Writes tests
#    - Commits changes
#    - Updates documentation
#    - Pushes to GitHub
#    - ALL AUTOMATICALLY

# 5. Cursor Bug Bot reviews PR
#    - Identifies issues
#    - Suggests improvements
#    - Claude Code fixes them
Illustrative conversation/workflow, not built-in shell commands.

What Claude Code Did Well

✅ Boilerplate code generation (models, services, widgets) ✅ Firebase integration (auth, Firestore, functions) ✅ TypeScript backend services ✅ Testing infrastructure ✅ Documentation maintenance ✅ Git workflow automation

Where Human Judgment Was Critical

🧠 Product decisions (which features, UX flows) 🧠 Architecture choices (Firebase vs custom backend) 🧠 Security considerations (Firestore rules, token storage) 🧠 Business logic edge cases (multi-currency calculations) 🧠 Design aesthetics (color palette, spacing, animations)

Development observations and measurement limits

  • Track complete accepted tasks and the review needed.
  • Separate elapsed time, active engineering time and model usage.
  • Compare against a matched baseline before claiming a speed multiplier.
  • Deployment and test counts do not establish production readiness.

AI assisted implementation, while architectural decisions, security review and acceptance testing remained engineering work. No controlled productivity multiplier is established here.

Technical Challenges & Solutions

Challenge 1: distinguish alerts from background updates

An iOS alert and a background refresh are different notification behaviors. Debug the app state, permission, APNs configuration and payload type rather than assuming one flag fixes every delivery failure.

The content-available flag requests background processing; it is not generally required to display an alert. Background delivery can be throttled or skipped and does not guarantee that code runs when the app is closed.

Illustrative Firebase Admin SDK configuration for a silent background update:

// Firebase Admin SDK: silent background update, not an alert.
apns: {
  headers: {
    "apns-push-type": "background",
    "apns-priority": "5"
  },
  payload: { aps: { contentAvailable: true } }
}
// Configure the iOS background capability and handler separately.
// Delivery and background execution are not guaranteed.

Verify alert presentation and background refresh separately on a physical device. Background modes, permissions, force-quit state and system scheduling can affect behavior.

Challenge 2: Multi-Currency Balance Calculations

Binary floating-point arithmetic can introduce monetary rounding errors. Rounding a USD balance to a whole double discards cents; use integer minor units or a suitable decimal representation with explicit currency rules.

Illustrative integer-cent arithmetic for USD. Parse decimal input exactly at the boundary, check supported ranges and apply an explicit rounding rule to currency conversion:

// Illustrative USD arithmetic: integer cents, not rounded dollars.
// Values below are synthetic. 1,025 cents + 35 cents = 1,060 cents.
int balanceUSDCents = 1025;
const int amountUSDCents = 35;
balanceUSDCents += amountUSDCents;
assert(balanceUSDCents == 1060);

// On confirmed review, persist the transaction, posting marker and balance change
// atomically on the trusted backend. Conversions need explicit
// currency scales, rate timestamps and a documented rounding rule.

Test fractional units, negative amounts, reversals, duplicate events, currency conversion and concurrent updates. A test count alone is not evidence that the money model is correct.

Challenge 3: Gmail OAuth Token Management

Problem: Tokens expire, Pub/Sub watch expires after 7 days, users lose email processing capability.

Solution:

  • Scheduled hourly token refresh
  • Automatic Pub/Sub watch renewal
  • Graceful fallback if refresh fails
  • User notification when re-auth needed

Challenge 4: Email Parsing Variability

Problem: Banks change email formats, different languages (Spanish/English), inconsistent merchant names.

Solution:

  • Use deterministic parsing where formats are stable and model assistance where it is justified.
  • Reuse reviewed corrections as selected context.
  • Treat model confidence as uncalibrated until evaluated.
  • Require review for uncertain, invalid or policy-sensitive records.

Model-based parsing and deterministic rules solve different problems. Evaluate them on representative formats and route uncertain or invalid results to review.

System Architecture

Here's how all the pieces fit together:

graph TB
    subgraph "User Device - iOS"
        A[FinHub Flutter App]
        B[FCM SDK]
        C[Firestore SDK]
    end

    subgraph "Firebase Cloud"
        D[Firebase Auth]
        E[Cloud Firestore]
        F[Cloud Functions]
        G[Cloud Messaging]
    end

    subgraph "External APIs"
        H[Gmail API]
        I[Gmail Pub/Sub]
        J[Claude AI API]
    end

    A --> D
    A --> C
    C --> E
    B --> G

    H --> I
    I --> F
    F --> E
    F --> J
    F --> G
    G --> B

    style A fill:#bb9af7
    style E fill:#9ece6a
    style F fill:#e0af68
    style J fill:#bb9af7

Email Processing Flow

sequenceDiagram
    participant Bank
    participant Gmail
    participant PubSub as Gmail Pub/Sub
    participant CF as Trusted Backend
    participant Claude as Claude AI
    participant FS as Firestore
    participant FCM
    participant Phone as iPhone

    Bank->>Gmail: Send transaction email
    Gmail->>PubSub: Publish change signal
    PubSub->>CF: Enqueue mailbox synchronization
    CF->>Gmail: Synchronize history and fetch authorized message
    Gmail-->>CF: Email content
    CF->>CF: Parse and validate candidate
    CF->>Claude: Request category suggestion
    Claude-->>CF: Untrusted suggestion
    CF->>FS: Atomically persist deduplicated pending candidate and review outbox
    Note over CF,FS: Ledger and balance unchanged
    CF->>FCM: Deliver outbox notification with retry
    FCM-->>Phone: Review alert when delivery succeeds
    Phone->>CF: Confirm reviewed candidate and revision
    CF->>FS: Atomically post ledger and balance once, mark candidate confirmed
    FS-->>CF: Existing posting or new committed result

The result: an implementation to evaluate

What Got Built:

The implementation explored Flutter screens, Gmail change events, model-assisted categorization, currency conversion and mobile notifications. The architecture and code below describe responsibilities to verify; they do not certify the app as secure or production-ready.

Operational questions before production use:

  • Measure observed application availability separately from provider SLA terms.
  • Measure latency, retries and recovery on representative workloads.
  • Evaluate cost using published rates and measured resource categories.
  • Test capacity and correctness before making scalability claims.

User Experience:

  1. User makes purchase at grocery store
  2. Bank sends a notification according to its own timing.
  3. Gmail receives email
  4. Pub/Sub triggers Cloud Function
  5. Claude AI categorizes as "🥩 Groceries"
  6. A deduplicated pending candidate and review outbox entry are saved to Firestore; the balance is unchanged.
  7. Push notification appears on iPhone lock screen
  8. User taps notification → app opens to review
  9. User reviews amount, currency, wallet and category, then confirms or rejects the candidate.
  10. On confirmation, the trusted backend atomically posts the ledger record and balance change once and marks the candidate confirmed. Replays return the existing result.

The two-week development narrative is an account of the build process, not a controlled productivity experiment or evidence of complete operational readiness.

Lessons Learned & The Future of AI Development

Key Insights

1. AI Amplifies, Not Replaces

AI assisted code generation and integration. Human judgment remained necessary for architecture, UX, security and acceptance; no measured percentage attribution is established here.

The magic happens in the collaboration. AI generates, human validates. AI implements, human architects. AI tests, human designs.

2. Serverless = Speed

Firebase manages infrastructure components and provides scaling features. Application owners must configure access control, credential storage, backups, recovery, quotas and monitoring. Serverless does not make these responsibilities disappear.

Trade-off: Vendor lock-in and cold starts. But for a solo developer? Worth it.

3. AI-Native Product Design

Model categorization can use prior examples and handle varied descriptions; rules can be preferable for stable formats. Evaluate both. A model-reported confidence score is not a calibrated probability and must not automatically bypass review without validation.

This isn't "we added AI." This is "AI is fundamental to how the product works."

4. Testing is Non-Negotiable

Financial software needs tests for arithmetic, authorization, retries, idempotency and recovery. AI-generated tests are useful only when their assertions independently reflect the intended behavior.

5. Modern Stack = Modern Speed

  • Flutter: Beautiful iOS UI with minimal code
  • Firebase: Backend without backend
  • TypeScript: Type safety in Cloud Functions
  • Claude AI: Intelligent features without ML expertise

The stack matters. Choose tools designed for rapid iteration.

What This Means for Development

AI-assisted development can shorten some implementation steps. Production readiness depends on the system’s tested behavior and operating requirements, not the calendar alone.

A small team can prototype a substantial integration, while specialist review and operational support may still be necessary.

The AI-Native Era

We're entering a paradigm shift where:

  • Apps ARE intelligent (Claude AI categorization, not just UI)
  • Development IS AI-assisted (Claude Code, Cursor, Bug Bot)
  • Deployment needs validation (timing alone does not establish readiness)
  • Quality needs evidence (test behavior, recovery and authorization)

FinHub represents this future:

  • Built WITH AI (Claude Code for development)
  • Powered BY AI (Claude API for categorization)
  • Deployed ON serverless (Firebase scales automatically)
  • Developed over two weeks, with production readiness evaluated separately.

For Developers:

Use the tools to test a concrete product idea, then measure whether the implementation meets its requirements.

Start with a bounded prototype and explicit acceptance criteria. Add the deployment controls required by the data and actions the product handles.

For Founders:

Prototype speed and production readiness are different milestones. Plan for security review, correctness, observability and failure recovery alongside feature work.

A useful advantage is delivering a reliable result for the intended user, not merely being first to deploy.

The Cyberpunk Future is Now:

We're living in a world where:

  • Category suggestions can reuse selected reviewed examples.
  • Emails become structured data automatically
  • Notifications are asynchronous and can arrive late or fail.
  • One developer builds what used to require a team
  • AI writes itself into the product

Technical Deep Dive: Architecture & Code

Data Model

erDiagram
    USER ||--o{ PENDING_CANDIDATE : reviews
    PENDING_CANDIDATE ||--o| TRANSACTION : posts_once
    USER ||--o{ TRANSACTION : confirms
    USER ||--o{ WALLET : owns
    USER ||--o{ CATEGORY : defines
    USER ||--o{ KEYWORD_MAPPING : reviews

    PENDING_CANDIDATE {
        string id PK
        string userId FK
        string sourceKey
        int revision
        int amountMinorUnits
        string currency
        string state
    }

    TRANSACTION {
        string id PK
        string userId FK
        string candidateId FK
        string postingKey
        int amountMinorUnits
        string currency
        string category
        string walletId FK
        timestamp timestamp
        string note
        string type
        string aiConfidence
    }

    WALLET {
        string id PK
        string userId FK
        string name
        int balanceMinorUnits
        string currency
        bool isDefault
    }

    CATEGORY {
        string id PK
        string userId FK
        string emoji
        string description
        bool isProtected
    }

AI Learning Loop

graph TD
    A[Email Arrives] --> B[Validate extracted candidate]
    B --> C[Load selected authorized context]
    C --> D[Load reviewed correction examples]
    D --> E[Request category suggestion]
    E --> F[Validate suggestion and exact amount]
    F --> G[Persist deduplicated pending candidate and review outbox]
    G --> H[User reviews amount currency wallet and category]
    H --> I{Explicit confirmation}
    I -->|Confirm| J[Atomic one-time ledger and balance update]
    I -->|Reject| K[Mark rejected without monetary write]
    J --> L[Store reviewed example for later prompts]
    L --> C

    style E fill:#bb9af7
    style J fill:#9ece6a
    style L fill:#e0af68

Key Code: Cloud Function Email Processing

ILLUSTRATIVE PSEUDOCODE — not a deployable handler.

On an authenticated Gmail change signal:
  Resolve the authorized mailbox and enqueue synchronization.
  Serialize processing per mailbox using a durable worker/lease
  with fencing so an expired worker cannot advance its checkpoint.
  Read OAuth credentials from server-only storage.
  Read the last successfully processed Gmail history cursor.

  If no cursor exists:
    Perform the documented full-sync procedure for the required scope.
    Persist pending candidates idempotently and establish a valid baseline.
    Do not use the incoming notification cursor as an initial sync.
  Otherwise:
    Keep startHistoryId fixed at the saved cursor for this sync.
    Call users.history.list; consume every nextPageToken.
    For every added message:
      Fetch and validate the authorized message and exact monetary fields.
      Use mailbox identity + message ID as a stable ingestion key.
      Atomically persist a pending candidate and review-notification outbox,
      or recognize an existing candidate (including confirmed/rejected ones).
      Do not write a transaction or change any balance during ingestion.
    After all pages and pending writes succeed, checkpoint the final
    response's historyId under the same serialized/fenced ownership.
    User review may occur later; it must not hold the mailbox cursor open.
    Never advance the cursor past failed pending-candidate persistence.
  If Gmail returns 404 for an expired history cursor:
    Full-sync with the same pending-candidate idempotency guarantees.
  On transient failure:
    Retry without advancing the saved cursor.
  Deliver review outbox notifications separately with retry/deduplication.
  Renew the Gmail watch and reconcile periodically for missed signals.

On an authenticated confirmation of a candidate revision:
  Recheck ownership, wallet authorization, revision and exact monetary fields.
  In one database transaction:
    If the candidate is already confirmed, return its existing posting.
    Reject a stale revision or a candidate that is no longer pending.
    Create one ledger record under the stable candidate posting key.
    Apply the signed balance change and mark the candidate confirmed.
  Retry conflicts without applying a second balance change.
On rejection:
  Atomically mark the pending candidate rejected; do not post money.
  Later category-only corrections must not reapply the monetary amount.

Required implementation/tests: auth, lease fencing, full sync, pagination,
exact parsing, pending persistence, concurrent confirmation, replay,
rejection, stale revisions, atomic posting, outbox and recovery.
Algorithm sketch with required correctness boundaries; implementations are not supplied.

Key Code: Flutter Real-time Transaction Stream

class TransactionService {
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final FirebaseAuth _auth = FirebaseAuth.instance;

  /// Stream of transactions for current user
  Stream<List<Transaction>> streamTransactions({
    String? categoryFilter,
    String? walletFilter,
  }) {
    final user = _auth.currentUser;
    if (user == null) return Stream.value([]);

    Query query = _firestore
        .collection('transactions')
        .where('userId', isEqualTo: user.uid)
        .orderBy('timestamp', descending: true)
        .limit(100);

    // Apply filters
    if (categoryFilter != null) {
      query = query.where('category', isEqualTo: categoryFilter);
    }
    if (walletFilter != null) {
      query = query.where('walletId', isEqualTo: walletFilter);
    }

    return query.snapshots().map((snapshot) {
      return snapshot.docs.map((doc) {
        final data = doc.data() as Map<String, dynamic>;
        data['id'] = doc.id;
        return Transaction.fromJson(data);
      }).toList();
    });
  }
}

Tech Stack Summary

Layer Technology Purpose Why Chosen
Frontend Flutter 3.32.7 iOS app Single codebase, hot reload, native performance
Dart 3.8.1 Language Type-safe, async/await, great tooling
Backend Firebase Serverless Managed infrastructure; configure auth, quotas and recovery
Cloud Firestore NoSQL DB Real-time streams, offline support
Cloud Functions Compute TypeScript, event-driven, auto-scale
AI Claude Console Categorization Category suggestions from selected context
Email Gmail API Email fetch OAuth 2.0, reliable, well-documented
Gmail Pub/Sub Webhooks Asynchronous change signals and reconciliation
Dev Tools Claude Code AI coding Boilerplate generation, Git automation
Cursor AI editor Context completions, refactoring
Language TypeScript Functions Type safety, better tooling

Conclusion

The useful result is an inspectable integration: events become pending candidates, models propose categories, and users confirm or reject the monetary details before posting. Every automated write needs a defined correctness and recovery contract.

Flutter, Firebase and Claude provide useful building blocks. The architecture still needs exact money handling, authorized access, reliable synchronization and measured behavior before it can support production claims.

The AI-native era rewards builders. Not planners. Not waiters. Builders.