Bundul
Internal
Browse docs
Waiting for review

archive

Bundul Engineering Quality Plan

A frozen record of a decision at the time. Superseded by a new record rather than edited.

Written by the build · 2 September 2026

Bundul Engineering Quality Plan

Stack: NestJS · MongoDB · GraphQL · Passport/CPX · Plaid (Backend) · Expo · React Native · Apollo Client (Frontend)


1. Purpose & Scope

This plan defines how Bundul validates quality before merge and before release, across the full stack.

Backend scope: NestJS services, resolvers, Mongoose models, Passport/CPX and Plaid integrations, background jobs, webhooks.

Frontend scope: Expo/React Native screens, hooks, contexts, GraphQL consumers, navigation flows, native config.


2. Quality Operating Model

Risk Tiers

Tier Examples
Low UI-only tweak, copy, spacing, style-only
Medium Single-flow behavior change, one query/mutation update, one screen logic change
High Auth/Plaid/payments/navigation/resume/data-contract/perf-sensitive changes
Critical Security, billing, token flows, release config, native app lifecycle

Gate Policy

Tier Required before merge
Low Lint + focused manual test + reviewer checklist
Medium Lint + targeted unit/integration tests + smoke flows
High / Critical Full gate: unit + integration + E2E smoke + release checklist + rollback notes

Ownership

  • Feature owner: implements and runs required checks for their tier
  • Reviewer: verifies checklist evidence and risk coverage
  • Release captain: owns final go/no-go and rollback readiness

3. Testing Layers

The five layers apply to both stacks. Each layer maps to a shared run cadence and has BE/FE-specific targets and tooling.

Layer 1 — Unit Tests

Cadence: CI on every PR. Target: BE < 60s, FE < 90s.

What: Business logic and pure functions in isolation. All external dependencies mocked.

BE targets:

Service Logic to cover
PaymentOrchestrationService Fee calculation, discount application, utility subscription exclusion (emailAccessId + hasInvoice checks), split payment fee
PaymentRetryService Retry guard conditions — wrong type, already scheduled, max attempts
SubscriptionService subsNotPaidForInCurrentMonth filtering, split payment option calculation, due date range
SmartDueDateService Plaid stream filtering (active, monthly, predicted date, amount > $2), Claude prompt construction, JSON parsing, date validation (future dates only), fallback when AI returns fewer than 5 suggestions
DiscountService Code validation, default fallback, expiry
All 16 resolvers Guard enforcement, DTO mapping, error propagation

BE tooling: Jest + @nestjs/testing provider mocks + jest.fn()

FE targets:

Area Logic to cover
Auth Redirect reason formatting and auto-continue guards
Plaid Account grouping and reconnect token selection
Bundul Report Day mapping from getBRMonthCalendarView, upcoming badge logic from isPaid, ordinal labels
OneSub / Resume Resume decision tree precedence and storage edge handling
Hooks / Utils Pure mappers, date/range logic, ordinal logic

FE tooling: Jest + ts-jest + @types/jest


Layer 2 — Integration Tests

Cadence: CI on every PR. Target: BE < 3 minutes, FE < 3 minutes.

What: BE — multi-model DB operations against real Mongoose with in-memory MongoDB, no external HTTP. FE — screen state transitions with mocked API data.

BE scenarios:

Scenario What it verifies
deleteMyAccount cascade All 12 dependent models (UserSubscription, PlaidToken, BackgroundJob, etc.) deleted for that user only
Idempotency key deduplication Calling processPayment twice with the same key creates exactly one payment record
BackgroundJob state machine PENDING → PROCESSING → COMPLETED and PENDING → PROCESSING → FAILED → retry enqueued
Subscription lifecycle create → activate → hasPaidForThisMonth = true → cancel
Orphaned record cleanup Seeded orphan records across all models → cleanup mutation → assert only orphans removed
Webhook → subscription update Feed real webhook fixture payload → assert correct model fields updated

BE tooling: Existing createTestModule() + getTestMongoUri() helpers + mongodb-memory-server

FE scenarios:

Area What it verifies
Auth Login/signup redirect messaging and color state
Plaid Modal actions: Add more, No more to add, loading state
Bundul Report Calendar day visibility, selected-day rendering, CTA visibility states
OneSub / Resume Resume route outcomes for each scenario
Navigation Back-stack and gesture expectations

FE tooling: @testing-library/react-native + MockedProvider + msw (GraphQL mode, optional) + @react-navigation/native test utils


Layer 3 — Contract Tests

Cadence: CI on every PR.

What: Verify that external service integrations (BE) and GraphQL query consumers (FE) handle real API response shapes correctly.

BE — how it works: Run once against real Passport/Plaid sandbox in record mode → save responses as fixtures → all future runs replay via nock. Sandbox availability is irrelevant after initial recording.

BE scenarios:

Service Scenarios
Passport/CPX Charge success, charge failed, insufficient funds, card-ready webhook, card-settled webhook
Plaid syncTransactions success, getRecurringTransactions, empty result, 429 rate limit
Airtable getBundulPricing success, missing Bundul Fee key

BE tooling: nock + existing test/fixtures/ directory

FE — how it works: GraphQL codegen schema validation checks verify that query fields and nullability assumptions match the live schema. No live network in CI.

FE scenarios: Query shape/field presence for all critical queries (payment details, supported subscriptions, user profile, virtual card, due date range).

FE tooling: graphql-codegen + schema validation + @apollo/client/testing


Layer 4 — Staging Scenario Tests

Cadence: Nightly. Not on PRs — staging availability and test account contention make PR builds non-deterministic.

What: Full-stack read verification against a dedicated test account on staging. Tests the complete data pipeline with real connected account data.

Test account requirements:

  • Plaid bank account connected
  • Passport/CPX card issued
  • ≥ 1 active subscription
  • Existing payment history

BE scenarios:

Query / Mutation What it verifies
getUserProfile Account data loads correctly
getUserDataVersion Version counters are present and numeric
getUserPaymentDetails Payment records load correctly
getSupportedSubscriptionsForUser Full pricing pipeline with real discount state
getUserOneSubDueDateRange Returns 5 AI suggestions with date + explanation fields; suggestions array present in response; falls back to heuristic when AI fails
getUserVirtualCard Passport card data is retrievable
Write mutations (e.g. update due date) Assert state change → restore original (explicit teardown)

BE tooling: Axios GraphQL client + Jest + SMOKE_TEST_JWT env var

FE scenarios:

Flow What it verifies
Auth Login, signup, redirect with real staging credentials
Plaid link / relink Full account linking with staging Plaid sandbox
Bundul report Open + selected day with real transaction data
OneSub resume Resume and step navigation against real subscription state

FE tooling: Maestro + staging environment + staging test account


Layer 5 — Full Payment E2E

Cadence: Weekly or manually before major releases. Not on PRs — async webhook timing is non-deterministic and each run adds 5–10 minutes.

What: The complete async payment chain: processPayment → Passport charge → BackgroundJob created → Passport webhook fires → subscription status updated. FE verifies that the correct final state is reflected in the UI after async settlement.

What this covers that other layers don't:

  • The webhook endpoint is reachable from Passport's servers
  • The full async chain completes end-to-end with real timing
  • Subscription status reflects the correct final state after settlement
  • FE payment/onboarding/report transitions reflect async settlement impact

BE tooling: Passport sandbox account + staging environment + manual trigger

FE tooling: Maestro + staging environment


4. Coverage Map

Risk Layer responsible Stack
Fee calculation wrong Unit BE
Utility sub incorrectly included in payment Unit BE
Resume decision tree wrong Unit FE
Account deletion doesn't cascade Integration BE
Duplicate payment charged Integration — idempotency BE
Screen state wrong after mocked API response Integration FE
Passport response shape changes Contract BE
Plaid returns unexpected empty response Contract BE
GraphQL query field removed or renamed Contract FE
Real user's subscription state is wrong Staging scenario BE + FE
Webhook never reaches backend Payment E2E BE
Full payment chain breaks end-to-end Payment E2E BE + FE

5. Cross-Stack Contract Ownership

  • Backend owns the shape and semantics of all API fields.
  • Frontend owns rendering and state behavior for those fields.
  • Any field change (rename / nullability / meaning) requires:
    • A changelog note in the BE PR
    • A frontend consumer update in the FE PR
    • Contract checklist ticked in both PRs before merge

Contract Change Checklist (FE side)

For each touched query/mutation/subscription:

  • Field list verified against latest schema
  • Optional/nullable handling explicitly reviewed
  • Empty-state behavior validated
  • Loading/error state validated
  • Analytics/side effects unchanged or intentionally updated

6. Feature-Area Test Expectations

Auth

Layer BE FE
Unit Guard enforcement on auth resolvers Redirect reason formatting, auto-continue guards
Integration Login/signup redirect messaging and color state
E2E User-not-found → signup redirect flow (Maestro)

Plaid + Linked Accounts

Layer BE FE
Unit Plaid contract fixtures: syncTransactions, getRecurring, rate limit Account grouping and reconnect token selection
Integration Webhook → subscription update with real fixture payload Modal actions: Add more, No more to add, loading state
E2E Link account, dismiss, relink broken token (Maestro)

Bundul Report

Layer BE FE
Unit Day mapping from getBRMonthCalendarView, isPaid badge logic, ordinal labels
Integration Calendar day visibility, selected-day rendering, CTA states
E2E Profile → report tap, selected day open with large datasets (Maestro)
Performance Dataset stress profile (≥ 400 services/day, ≥ 500 monthly payments)

OneSub + Resume

Layer BE FE
Unit SubscriptionService filtering, split payment option, due date range; SmartDueDateService stream filtering + AI response parsing Resume decision tree precedence and storage edge handling
Integration Subscription lifecycle: create → activate → paid → cancel Resume route outcomes for each scenario
E2E Full payment chain Cold start resume, step 3 back behavior, no-supported route transitions

Payments

Layer BE FE
Unit Fee calc, discount, utility exclusion, retry guards
Integration Idempotency dedup, BackgroundJob state machine
Contract Passport charge success/fail/insufficient funds, webhooks
E2E Full async chain: processPayment → charge → webhook → status Payment status reflected in UI after settlement

7. Process Checklists

PR Checklist (Feature Owner)

Before requesting review:

  • Risk tier assigned (Low / Medium / High / Critical)
  • Affected paths listed
  • API field/contract changes documented (if any) — BE changelog + FE consumer update
  • Required tests for tier executed and passing
  • Manual smoke for touched user paths completed
  • Analytics/logging impact reviewed
  • Navigation/back behavior validated on iOS and Android (FE, where relevant)
  • For High/Critical: rollback strategy documented

PR Checklist (Reviewer)

  • No unrelated diff in changed files
  • Contract changes reflected in hooks + UI consumers
  • Edge cases covered (empty, error, stale, retry, duplicate data)
  • Rollback strategy present for high/critical changes
  • Checklist evidence provided by feature owner

Joint FE/BE Readiness Checklist

Before shipping high-risk changes (auth / Plaid / payment / resume / report):

  • Backend contract tests for touched endpoints are green
  • Frontend query consumers for those endpoints are verified
  • Staging scenario tested with real account fixtures
  • Resume/navigation behavior verified after real backend responses
  • Observability events on both FE and BE confirmed

Release Checklist (Preview → Production)

  • Build preview for iOS and Android
  • Execute release smoke flows:
    • Auth (login / signup / redirect)
    • Plaid link / relink / cancel
    • Bundul report open + selected day
    • OneSub resume and step navigation
    • Payment flow (if changed)
  • Validate monitoring dashboards and event names
  • Confirm no high-severity Sentry issues in preview run
  • Confirm rollback path (version/changelog owner)

Post-Release Checklist (24–48 hours)

  • Monitor crash-free session rate
  • Monitor funnel drop-offs on changed flows
  • Review top new errors/warnings
  • Confirm no unexpected navigation loop reports
  • Log follow-up fixes and assign owners

8. Diff Audit Prompt

Use this after major implementation, before merge. Paste the template plus your diff into an AI (Claude) to get a structured pre-merge risk review.

When to use: Any Medium/High/Critical PR, or any PR touching payments, auth, Plaid, navigation, or data contracts.

How to use:

  1. Fill in the context fields below.
  2. Paste the template + your git diff main...feature-branch output into Claude.
  3. Review the output — address all Must-fix items before merging.

Backend Diff Audit Prompt

You are doing a release-grade diff audit for the Bundul backend (NestJS · MongoDB · GraphQL · Passport/CPX · Plaid).

Context:
- Base branch: <base>
- Feature branch: <feature>
- Scope: <feature summary>
- Risk areas: <auth / payments / plaid / webhooks / data contract / background jobs>

Tasks:
1) List all changed files grouped by risk (high / medium / low).
2) Identify unrelated or accidental changes.
3) Validate API contract changes (renamed fields, nullability, removed fields) and all downstream GraphQL consumers.
4) Detect missing guard enforcement on new resolvers or mutations.
5) Detect missing idempotency handling on any new payment or charge paths.
6) Detect missing unit or integration tests for changed business logic.
7) Check for new console.log calls, hardcoded secrets, or disabled auth guards.
8) Output:
   - Must-fix before merge
   - Should-fix soon
   - Safe to defer
   - Targeted verification commands
   - Rollback notes

Frontend Diff Audit Prompt

You are doing a release-grade diff audit for the Bundul mobile app (Expo · React Native · Apollo Client · GraphQL).

Context:
- Base branch: <base>
- Feature branch: <feature>
- Scope: <feature summary>
- Risk areas: <auth / plaid / navigation / resume / data contract / performance>

Tasks:
1) List all changed files grouped by risk (high / medium / low).
2) Identify unrelated or accidental changes.
3) Validate API contract changes and all downstream hook/UI consumers.
4) Detect navigation/back-stack regressions.
5) Detect performance regressions (large lists, expensive memo misses, unnecessary re-renders).
6) Check for missing empty-state, error-state, or loading-state handling.
7) List missing tests/checks for this change set.
8) Output:
   - Must-fix before merge
   - Should-fix soon
   - Safe to defer
   - Targeted verification commands
   - Rollback notes

9. What Is Not Automated

Operation Reason Approach
OTP delivery (Infobip SMS) Cannot assert receipt of real SMS in CI Unit test that sendSms is called with correct params; manual verification with team phone once per environment setup
cleanupOrphanedUserRecords in production Destructive, requires human review before execution Covered fully in integration tests; manual execution only in production
First-time user payment (fresh account) Requires a disposable test account Covered by Layer 5 using a throwaway staging account
E2E on PRs Async webhook timing non-deterministic; Passport sandbox quota limits; adds 5–10 min Weekly scheduled run + manual trigger before major releases

Edge Cases That Must Be Covered (but easy to miss)

  • Report previously existed, now empty
  • Resume state in storage conflicts with backend facts
  • Token invalidation while app is backgrounded
  • iOS back gesture from stateful detail screen
  • Duplicate merchant names across multiple accounts
  • High-volume payment datasets causing UI stalls (≥ 400 services/day)

10. Rollout Roadmap

Backend Phases

Phase Work Confidence gained
1 Record Passport + Plaid sandbox responses; extend webhook fixtures Unblocks all contract tests
2 Unit tests for payment orchestration and retry logic Regression safety on payment logic
3 Integration tests for cascade delete, idempotency, job state machine Confidence on the riskiest DB operations
4 Set up staging test account + reset mutation + nightly run Full-stack verification with real connected account data
5 Audit and fix existing 14 spec files Close gaps in current coverage baseline
6 Payment E2E on staging + schedule weekly run End-to-end production parity

Frontend Fast-Track (Day-0, 6–8 hours total)

Block Work Effort
1 — Foundation Confirm lint + typecheck run locally and in CI; add PR checklist link; define risk-tier labels 1.5–2 hours
2 — High-Risk Smoke Pack Create/confirm manual smoke script for auth redirect, Plaid link/dismiss/reconnect, report open, OneSub resume 2–2.5 hours
3 — Diff Audit Process Adopt diff audit prompt for all Medium/High PRs; require must-fix/should-fix/defer output before merge; require rollback notes for High/Critical 1–1.5 hours
4 — Minimum Automated Coverage Enforce npm run lint + npx tsc --noEmit + targeted unit/integration tests for changed high-risk logic 1.5–2 hours

Optional Hardening (Later, both stacks)

  • Expand Maestro E2E automation beyond core flows
  • Add performance regression suite for Bundul Report
  • Set up contract test recording pipeline (nock)
  • Broaden coverage baseline audit

11. CI Pipeline

Backend (every PR)

npm run lint
npx tsc --noEmit
jest --testPathPattern=unit
jest --testPathPattern=integration
jest --testPathPattern=contract    # after Phase 1 fixtures are recorded

Frontend (every PR)

npm run lint
npx tsc --noEmit
jest --testPathPattern=unit
jest --testPathPattern=integration  # once configured

Nightly (both stacks)

Staging scenario tests (Layer 4) — BE + FE against staging test account

Weekly / Pre-Release (both stacks)

Full payment E2E (Layer 5) — manual trigger or scheduled
FE Maestro E2E smoke pack

12. Maintenance Cadence

  • Weekly: Flaky test review + top new Sentry error review
  • Per release: Full release checklist + diff audit on all High/Critical PRs
  • Monthly: Update edge-case catalog, remove stale tests, review coverage gaps

This is a record of a decision at the time. It is not edited — write a new record that supersedes it.