Skip to content

test: fix harness failures and update manifest#393

Merged
Connorbelez merged 19 commits intomainfrom
test-fixes
Apr 14, 2026
Merged

test: fix harness failures and update manifest#393
Connorbelez merged 19 commits intomainfrom
test-fixes

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Apr 11, 2026

Summary by Sourcery

Update tests and harnesses across e2e and Convex suites to align with current auth, routing, audit log, and payments behavior, and add a manifest documenting historical test failures and coverage.

Bug Fixes:

  • Adjust governed transitions e2e selectors and helpers to reduce flakiness and reflect current UI roles and labels.
  • Fix onboarding e2e auth handling by generating fresh storage state per org, validating the active org and role, and using the configured test account email.
  • Stabilize marketplace simulation settlement flow by explicitly handling both success and remaining-balance validation outcomes.
  • Align CRM user-saved-views tests with current admin-only permissions and computed field metadata behavior.
  • Update collection plan, transfer reconciliation, webhook, and settlement tests to match the current escalation and cash-receipt posting semantics.
  • Fix hash-chain reconciliation tests by seeding complete audit journal documents and asserting canonical envelope metadata.
  • Correct demo listings tests to use a current featured listing fixture and its dynamic map copy.
  • Make protected routes, WorkOS profile, and RBAC e2e tests assert against the current AuthKit host, session role badge layout, and email card structure.

Enhancements:

  • Refactor Convex audit-log and aggregate component registration in tests to use the official test registrar helper.
  • Introduce shared helpers for locating governed transition entity cards, stat cards, and admin pages to simplify e2e specs.
  • Add payment rule contract tests for typed schedule and retry configs using the new config field.
  • Relax collection plan seeding helpers to optionally allow mixed-mortgage obligations when explicitly requested.
  • Tighten collection attempt machine tests to assert settlement observation actions for FUNDS_SETTLED events in the confirmed state.
  • Seed richer transfer and account context in payments webhook tests to exercise EFT VoPay and provider-owned transfer flows more realistically.
  • Use fake timers in deal integration tests to drain scheduled work deterministically and avoid timer leakage across cases.
  • Add RBAC e2e helper for asserting the resolved role label next to the Role field.

Documentation:

  • Add a test failure manifest documenting historical Vitest and Playwright failures, their root cause classification, and which were addressed by this harness pass.

Tests:

  • Mark document engine e2e suites to run with admin storage state and add admin bootstrap helpers for authenticated contexts and setup flows.
  • Update document engine template, library, and designer e2e workflows to create and clean up PDFs and templates via authenticated helpers.
  • Adjust onboarding, login, RBAC, and protected-route e2e tests to rely on centralized auth storage helpers and environment-driven org ids.
  • Refine collection plan execution tests to allow explicit mixed-obligation scenarios while keeping default mortgage consistency checks.
  • Extend collection attempt machine tests to assert settlement-observation actions on FUNDS_SETTLED events.
  • Update collection attempt reconciliation integration tests to expect both transfer-level and obligation-level cash receipts.
  • Broaden CRM view engine tests to assert stable fieldDefIds for computed verificationSummary fields.
  • Tighten transfer reconciliation tests to assert escalation state via transferHealingAttempts instead of suspense journal entries.

Summary by CodeRabbit

  • New Features

    • Enhanced authorization system with new admin:access permission serving as a system-wide override
    • Manual payment application workflow for obligations with settlement tracking
  • Bug Fixes

    • Improved transfer settlement confirmation and reversal handling
    • Enhanced cash receipt tracking and reconciliation for payment processing
    • Corrected payment access validation for lenders, borrowers, and obligations
  • Improvements

    • Strengthened authorization checks across payment resources and transfers
    • Better isolation of transfer-specific ledger entries and posting groups

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Warning

Rate limit exceeded

@Connorbelez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 16 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 25 minutes and 16 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1142c1d2-2ac9-4be8-ab73-b4a181cbeb32

📥 Commits

Reviewing files that changed from the base of the PR and between 7d28837 and 7667859.

📒 Files selected for processing (19)
  • convex/auth/__tests__/resourceChecks.test.ts
  • convex/auth/resourceChecks.ts
  • convex/engine/commands.ts
  • convex/payments/collectionPlan/__tests__/admin.test.ts
  • convex/payments/collectionPlan/__tests__/manualCollection.test.ts
  • convex/payments/collectionPlan/__tests__/workout.test.ts
  • convex/payments/collectionPlan/initialScheduling.ts
  • convex/payments/collectionPlan/manualCollection.ts
  • convex/payments/collectionPlan/workout.ts
  • convex/payments/webhooks/__tests__/stripeWebhook.test.ts
  • convex/payments/webhooks/stripe.ts
  • convex/schema.ts
  • e2e/auth/onboarding.spec.ts
  • e2e/helpers/auth-storage.ts
  • e2e/helpers/document-engine.ts
  • src/lib/auth.ts
  • src/routes/admin/underwriting/route.tsx
  • src/routes/e2e/session.tsx
  • src/test/auth/route-guards.test.ts
📝 Walkthrough

Walkthrough

This PR introduces authorization resource checks for payment entities, implements a manual inbound collection flow for obligations via collection plans, refactors payment settlement pipelines to support transfer-based cash receipts, simplifies admin permissions to a wildcard "admin:access" model, and establishes e2e testing infrastructure for session validation.

Changes

Cohort / File(s) Summary
Authorization Resource Checks
convex/auth/resourceChecks.ts, convex/auth/__tests__/resourceChecks.test.ts
Added seven new exported authorization helpers (canAccessLenderEntity, canAccessBorrowerEntity, canAccessObligation, canAccessCounterpartyResource, canAccessTransferRequest, canAccessCashLedgerAccount, canAccessWorkoutPlan) with comprehensive access rules and test coverage for payment/cash-ledger entities.
Permission Model Refactoring
convex/fluent.ts, src/lib/auth.ts, src/test/auth/permissions.ts, src/test/auth/chains/role-chains.test.ts, src/test/auth/permissions/new-permissions.test.ts, src/test/auth/middleware/requirePermission.test.ts, src/test/auth/route-guards.test.ts
Introduced ADMIN_ACCESS_PERMISSION ("admin:access") as a wildcard permission and updated requirePermission checks to treat admin access as satisfying any permission requirement. Simplified admin role to single permission and expanded EXTERNAL_ORG_ADMIN access across multiple endpoints.
Manual Collection Implementation
convex/payments/collectionPlan/manualCollection.ts, convex/payments/collectionPlan/executionContract.ts, convex/payments/collectionPlan/execution.ts, convex/payments/collectionPlan/mutations.ts, convex/payments/collectionPlan/initialScheduling.ts
Introduced new manual inbound collection module with runManualInboundCollectionForObligation function, extended execution contracts to accept optional manualSettlement field, and wired manual settlement into plan entry creation and execution.
Cash Ledger & Settlement Refactoring
convex/payments/cashLedger/queries.ts, convex/payments/cashLedger/integrations.ts, convex/payments/cashLedger/__tests__/transferReconciliation.test.ts
Added access-check assertions to cash ledger queries, updated postPaymentReversalCascade to handle transferRequestId-based matching with ambiguity detection, and refactored reconciliation tests to validate transfer-based settlement flows.
Transfer & Payment Pipeline
convex/engine/commands.ts, convex/engine/effects/collectionAttempt.ts, convex/engine/effects/obligationPayment.ts, convex/engine/effects/__tests__/transfer.test.ts, convex/payments/transfers/collectionAttemptReconciliation.ts, convex/payments/transfers/queries.ts, convex/payments/transfers/collectionAttemptReconciliation.integration.test.ts
Changed confirmObligationPayment from mutation to action calling runManualInboundCollectionForObligation, updated payment effects to include transferRequestId in payloads, added transfer-based ledger entry matching, and wired authorization checks into transfer queries.
Collection Plan & Execution
convex/payments/collectionPlan/__tests__/admin.test.ts, convex/payments/collectionPlan/__tests__/execution.test.ts, convex/payments/collectionPlan/__tests__/ruleContract.test.ts, convex/payments/collectionPlan/workout.ts
Updated test setup to use audit logs and fetch data directly instead of admin APIs, added allowMixedMortgageObligations option, updated rule contract defaults, and added access assertions to workout plan queries.
Demo Simulation
convex/demo/simulation.ts
Converted triggerDispersal from mutation to action, changed implementation to call runManualInboundCollectionForObligation, expanded cleanup to include transfer requests and cash ledger artifacts.
Webhook & Transfer Provider Handling
convex/payments/webhooks/stripe.ts, convex/payments/webhooks/__tests__/stripeWebhook.test.ts, convex/payments/webhooks/__tests__/eftVopayWebhook.test.ts, convex/payments/webhooks/__tests__/reversalIntegration.test.ts, convex/payments/webhooks/__tests__/vopayWebhook.test.ts, convex/payments/transfers/providers/__tests__/registry.test.ts
Added persistence and deferred processing for unsupported Stripe reversals, updated test seeding to include cash-ledger accounts and obligation fixtures, and confirmed Rotessa provider resolution.
CRM & View Engine
convex/crm/__tests__/userSavedViews.test.ts, convex/crm/__tests__/viewEngine.test.ts
Changed user-owned saved view tests to expect admin-only access, updated computed field identifier assertion in view schema test.
E2E Testing Infrastructure
e2e/helpers/auth-storage.ts, e2e/helpers/document-engine.ts, e2e/auth/login.spec.ts, e2e/auth/onboarding.spec.ts, e2e/auth/protected-routes.spec.ts, e2e/rbac/admin.spec.ts, e2e/rbac/member.spec.ts, e2e/rbac/helpers.ts, playwright.config.ts, src/routes/e2e/session.tsx, src/routeTree.gen.ts
Introduced new /e2e/session endpoint for auth validation, added admin storage state management, created expectResolvedRole helper, updated document-engine tests to use admin auth, created fresh per-test auth storage, and updated Playwright config with dedicated document-engine project.
Page Automation & Navigation
e2e/demo-listings.spec.ts, e2e/governed-transitions.spec.ts, e2e/simulation.spec.ts, e2e/document-engine/*.spec.ts
Refactored listing/entity navigation with helper functions, improved element locator specificity (role-based selectors, exact-text matching), added settlement outcome waiting, and consolidated admin page setup across document-engine suites.
Route Guards & Auth
src/routes/_authenticated/authenticated.tsx, src/routes/demo/convex.tsx, src/routes/demo/simulation.tsx
Added guardAuthenticated beforeLoad hooks to routes, changed demo simulation to use useAction instead of useMutation for triggerDispersal.
Test Utilities & Helpers
src/test/convex/hash-chain-reconciliation.test.ts, src/test/convex/payments/helpers.ts, convex/test/registerAuditLogComponent.ts, src/test/convex/registerAuditLogComponent.ts, convex/machines/__tests__/deal.integration.test.ts
Added audit journal entry builder helper, extended plan-entry seeding with mixed-mortgage option, simplified audit-log registration via library call, and added fake timer setup for deal integration tests.
Configuration & Documentation
.gitignore, AGENTS.md, CLAUDE.md, docs/test-failure-manifest.md
Added .tmp/ to gitignore, updated GitNexus metadata indices, and documented test failure analysis and failure categories.
Bank Account Queries
convex/payments/bankAccounts/queries.ts
Added counterparty access assertion to listBankAccountsByOwner query.
Metadata & Sync
src/test/auth/permissions/permission-metadata-sync.test.ts
Extended KNOWN_ORPHANS registry with new permission metadata keys and justifications.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • eng-185 (#283) #286: Related through overlapping modifications to transfer/payment domain (transfer effects, transferRequests queries/mutations, cash-ledger posting, webhook handling and reconciliation).
  • collection attempt execution spine #388: Directly related—both modify payment/collection-plan and transfer reconciliation code paths, attempt-linked inbound flows, rule contracts, and related engine/effects.
  • collection attempt execution spine #391: Related through changes to transfer/manual-settlement flows, collection-plan/manual inbound collection wiring, and audit/journal-related fields and helpers.

Poem

🐰 A rabbit hops through authorization lands,
With admin:access in capable hands,
Cash settled swiftly on transfer routes,
E2E sessions sprouting fresh shoots,
Collections flow smoothly—no disputes!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test: fix harness failures and update manifest' accurately reflects the primary changes: it fixes multiple test harnesses and adds a test-failure manifest documentation file.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test-fixes

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.

@Connorbelez Connorbelez marked this pull request as ready for review April 11, 2026 21:57
Copilot AI review requested due to automatic review settings April 11, 2026 21:57
Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 11, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 11, 2026

Reviewer's Guide

Stabilizes Vitest and Playwright suites by updating test harnesses, fixtures, and expectations to match current auth, audit-log, document engine, and payments behavior, while introducing shared helpers and a test-failure manifest documenting prior instability.

Sequence diagram for onboarding auth storage state creation and role review flow

sequenceDiagram
    actor Member
    actor Admin
    participant Playwright as PlaywrightTests
    participant AuthStorage as AuthStorageHelper
    participant Browser
    participant AuthKit as AuthKitHostedLogin
    participant App as AppBackend

    Member->>Playwright: run onboarding.spec member tests
    Playwright->>AuthStorage: createAuthStorageState(orgId = TEST_MEMBER_ORG_ID)
    AuthStorage->>Browser: browser.newContext()
    Browser-->>AuthStorage: Context
    AuthStorage->>AuthKit: loginViaWorkOS(testAccountEmail)
    AuthKit-->>AuthStorage: authenticated session
    AuthStorage->>App: GET /e2e/switch-org?orgId=member
    App-->>AuthStorage: redirect to /
    AuthStorage->>App: GET /demo/workos
    AuthStorage->>App: open Organizations tab
    App-->>AuthStorage: session card with orgId and role member
    AuthStorage->>Browser: context.storageState(path)
    AuthStorage-->>Playwright: storageState path

    Playwright->>Browser: browser.newContext({ storageState })
    Browser-->>Playwright: MemberContext
    Playwright->>MemberContext: open /demo/rbac-auth/onboarding
    MemberContext-->>Playwright: onboarding UI loaded
    Member->>MemberContext: submit role request
    MemberContext->>App: POST /onboarding/request
    App-->>MemberContext: request pending

    Admin->>Playwright: run admin onboarding tests
    Playwright->>AuthStorage: createAuthStorageState(orgId = TEST_ADMIN_ORG_ID)
    AuthStorage->>AuthKit: loginViaWorkOS(testAccountEmail)
    AuthStorage->>App: switch org to admin, verify orgId and role admin
    AuthStorage-->>Playwright: admin storageState path
    Playwright->>Browser: browser.newContext({ storageState })
    Browser-->>Playwright: AdminContext
    Playwright->>AdminContext: open /demo/rbac-auth/onboarding
    AdminContext->>App: GET pending requests
    App-->>AdminContext: pending request for testAccountEmail

    Admin->>AdminContext: click Approve or Reject
    AdminContext->>App: POST /onboarding/review
    App-->>AdminContext: success or error
    AdminContext-->>Playwright: UI update (card removed or error banner)
    Playwright-->>Admin: expect() assertions pass
Loading

Sequence diagram for document engine admin test helper and storage state reuse

sequenceDiagram
    actor QAEngineer
    participant Playwright as DocumentEngineTests
    participant DocHelper as DocumentEngineHelper
    participant AuthStorage as AuthStorageHelper
    participant Browser
    participant App as DocumentEngineApp

    QAEngineer->>Playwright: run document-engine templates.spec

    Note over Playwright,Browser: beforeAll setup via openAdminPage
    Playwright->>DocHelper: openAdminPage(browser)
    DocHelper->>Browser: browser.newContext()
    Browser-->>DocHelper: Context
    DocHelper->>Browser: Context.newPage()
    Browser-->>DocHelper: Page
    DocHelper->>AuthStorage: createAuthStorageState(orgId = TEST_ADMIN_ORG_ID, page, path = ADMIN_STORAGE_STATE)
    AuthStorage->>App: AuthKit login + switch org to admin
    AuthStorage->>Browser: context.storageState(path)
    DocHelper-->>Playwright: { context, page }
    Playwright->>DocHelper: uploadTestPdf(page, pdfName)
    DocHelper->>App: POST /demo/document-engine/library upload
    App-->>DocHelper: PDF stored
    DocHelper->>Browser: context.close()

    Note over Playwright,Browser: per-test contexts reuse ADMIN_STORAGE_STATE
    loop each test case
        Playwright->>Browser: browser.newContext({ storageState: ADMIN_STORAGE_STATE })
        Browser-->>Playwright: AuthenticatedContext
        Playwright->>AuthenticatedContext: newPage()
        AuthenticatedContext-->>Playwright: Page
        Playwright->>Page: goto /demo/document-engine/... (templates, groups, variables, generate, library, navigation, designer)
        Page->>App: authorized GET/POST requests
        App-->>Page: protected admin UIs
        Playwright-->>QAEngineer: expect() assertions pass
        Playwright->>AuthenticatedContext: context.close()
    end

    Note over Playwright,Browser: afterAll cleanup via openAdminPage
    Playwright->>DocHelper: openAdminPage(browser)
    DocHelper->>Browser: browser.newContext()
    Browser-->>DocHelper: Context
    DocHelper->>Browser: Context.newPage()
    Browser-->>DocHelper: Page
    Playwright->>Page: goto /demo/document-engine/library
    Playwright->>Page: delete test PDF if present
    Page->>App: DELETE library asset
    App-->>Page: asset deleted
    Playwright->>Browser: context.close()
Loading

Sequence diagram for simulation settlement outcome handling and retry logic

sequenceDiagram
    actor Tester
    participant Playwright as SimulationTest
    participant Helper as SettlementHelper
    participant Browser
    participant App as SimulationApp

    Tester->>Playwright: run Marketplace Simulation — Full Flow
    Playwright->>Browser: open obligation row
    Playwright->>Browser: fill Settled amount (¢) with remainingPaymentAmount
    Playwright->>Browser: click Apply Payment

    Playwright->>Helper: waitForSettlementOutcome(page)
    alt success message first
        Helper->>Browser: waitFor "Payment applied. Dispersal scheduled."
        Browser-->>Helper: visible
        Helper-->>Playwright: outcome = success
        Playwright->>Browser: expect success message visible
    else balance error first
        Helper->>Browser: waitFor text /exceeds remaining balance/
        Browser-->>Helper: visible
        Helper-->>Playwright: outcome = error
        Playwright->>Browser: expect error message visible
        Playwright->>Browser: read obligationAmountCell innerText()
        Browser-->>Playwright: formatted amount
        Playwright->>Playwright: retryAmount = currencyTextToCents(amount)
        Playwright->>Browser: click Pay on obligationRow
        Playwright->>Browser: waitFor Settled amount (¢) input
        Playwright->>Browser: fill retryAmount
        Playwright->>Browser: click Apply Payment
        Playwright->>Browser: wait for success message visible
    end

    Playwright->>Browser: expect obligationRow toHaveCount(0)
    Browser-->>Playwright: assertion passes
    Playwright-->>Tester: settlement flow verified without flakes
Loading

Class diagram for updated Playwright test helper modules and usages

classDiagram
    class AuthStorageHelper {
      +string TEST_ADMIN_ORG_ID
      +string TEST_MEMBER_ORG_ID
      +createAuthStorageState(orgId string, page Page, path string) Promise<void>
      +requireEnv(name string) string
    }

    class DocumentEngineHelper {
      +string BASE_URL
      +string ADMIN_STORAGE_STATE
      +uniqueKey(prefix string) string
      +openAdminPage(browser Browser) Promise<OpenAdminPageResult>
      +uploadTestPdf(page Page, pdfName string) Promise<void>
      +uniqueName(prefix string) string
    }

    class RBACHelpers {
      +expectResolvedRole(page Page, role string) Promise<void>
    }

    class LoginSpec {
      -string testEmail
      +getProfileEmailCard(page Page) Locator
    }

    class OnboardingSpec {
      -string onboardingAuthStateDir
      -string testAccountEmail
      +createFreshStorageState(browser Browser, orgId string) Promise<string>
      +openOnboardingPage(browser Browser, orgId string) Promise<OnboardingSession>
      +clearPendingRequests(browser Browser) Promise<void>
    }

    class SimulationHelper {
      +currencyTextToCents(value string) number
      +waitForSettlementOutcome(page Page) Promise~string~
    }

    class GovernedTransitionsHelpers {
      +goToTab(page Page, path string, heading string) Promise<void>
      +resetDemo(page Page) Promise<void>
      +createApplication(page Page, opts CreateApplicationOpts) Promise<void>
      +getEntityCard(page Page, label string) Locator
      +getStatCard(page Page, label string) Locator
    }

    AuthStorageHelper <.. OnboardingSpec : uses
    AuthStorageHelper <.. DocumentEngineHelper : uses

    DocumentEngineHelper <.. DocumentEngineSpecs : applied_via_test.use
    class DocumentEngineSpecs {
      +beforeAll(browser Browser) void
      +tests_use_storageState_ADMIN_STORAGE_STATE
    }

    RBACHelpers <.. RBACAdminSpec : expectResolvedRole
    RBACHelpers <.. RBACMemberSpec : expectResolvedRole
    class RBACAdminSpec {
      +admin session shows admin role
    }
    class RBACMemberSpec {
      +member session shows member role
    }

    LoginSpec <.. AuthTests : verifies profile email and sign out
    OnboardingSpec <.. AuthTests : onboarding role request scenarios

    SimulationHelper <.. SimulationSpec : settlement flow assertions

    GovernedTransitionsHelpers <.. GovernedTransitionsSpec : layout and UC tests

    class AuthTests {
      +authenticated user sees profile on workos demo page
      +sign out clears session
      +protected routes redirect to AuthKit hosted page
    }

    class SimulationSpec {
      +Marketplace Simulation — Full Flow
    }

    class GovernedTransitionsSpec {
      +Layout and navigation tests
      +Use cases UC_1_to_Reactive
    }
Loading

File-Level Changes

Change Details Files
Harden Playwright governed-transitions and simulation specs with role-aware selectors and deterministic settlement handling.
  • Introduce helper locators for governed transition entity and stat cards and replace broad text queries with role-based and exact-match locators.
  • Adjust navigation and heading expectations to use semantic roles (links, headings, buttons) and explicit timeouts.
  • Refactor simulation settlement flow to wait on either success or validation error and then retry deterministically based on table state.
e2e/governed-transitions.spec.ts
e2e/simulation.spec.ts
Align Convex audit, hash-chain, CRM, collection-plan, transfer reconciliation, and webhook tests with current domain models and component registration.
  • Use convex-audit-log test helper to register the audit-log component instead of manual module maps and re-export the shared registrar for src tests.
  • Add builders and richer fixtures for auditJournal and auditTrail envelopes so hash-chain reconciliation and metadata serialization match production shapes.
  • Update CRM saved-view and view-engine tests to assert current admin-only access control and computed-field identifiers.
  • Adapt collection plan rule contract tests and execution helpers to use typed config and an option flag for mixed-mortgage obligations.
  • Update transfer reconciliation and webhook fixtures to reflect escalation semantics and required transfer/mortgage context, and adjust expectations accordingly.
convex/test/registerAuditLogComponent.ts
src/test/convex/registerAuditLogComponent.ts
src/test/convex/engine/hash-chain-reconciliation.test.ts
convex/crm/__tests__/userSavedViews.test.ts
convex/crm/__tests__/viewEngine.test.ts
src/test/convex/payments/helpers.ts
convex/payments/collectionPlan/__tests__/ruleContract.test.ts
convex/payments/collectionPlan/__tests__/execution.test.ts
convex/payments/cashLedger/__tests__/transferReconciliation.test.ts
convex/payments/webhooks/__tests__/eftVopayWebhook.test.ts
convex/payments/webhooks/__tests__/vopayWebhook.test.ts
convex/payments/transfers/__tests__/collectionAttemptReconciliation.integration.test.ts
Refactor Playwright auth and RBAC harnesses to use dynamic AuthKit flows, org-scoped storage state, and shared role assertions.
  • Introduce an auth-storage helper that logs in via WorkOS/AuthKit, switches orgs by env-driven ids, and verifies session context before persisting storage state.
  • Update onboarding, document engine, login, protected-routes, and RBAC specs to create fresh, role-specific storage states at runtime and assert roles via a shared helper.
  • Adjust protected-routes and login tests to match the current AuthKit host pattern and profile DOM structure.
e2e/helpers/auth-storage.ts
e2e/auth/onboarding.spec.ts
e2e/helpers/document-engine.ts
e2e/document-engine/*.spec.ts
e2e/auth/login.spec.ts
e2e/auth/protected-routes.spec.ts
e2e/rbac/admin.spec.ts
e2e/rbac/member.spec.ts
e2e/rbac/helpers.ts
Stabilize machine and engine tests with proper timer control and updated state-machine expectations.
  • Wrap deal integration tests with fake timer setup/teardown to ensure scheduled effects complete deterministically.
  • Update collectionAttempt machine tests to expect recordSettlementObserved actions when FUNDS_SETTLED is received in the confirmed state.
convex/machines/__tests__/deal.integration.test.ts
convex/engine/machines/__tests__/collectionAttempt.test.ts
Update demo listings and document engine e2e specs to use stable fixtures and centralized admin bootstrap.
  • Use shared listing-detail mocks to derive expected titles and map text instead of hard-coded slugs, and assert mobile copy based on fixture data.
  • Introduce an openAdminPage helper and a shared admin storage-state file, and use them across document engine specs for setup/teardown and navigation tests.
e2e/demo-listings.spec.ts
e2e/helpers/document-engine.ts
e2e/document-engine/*.spec.ts
Document historical test instability and the post-fix status via a test failure manifest.
  • Add a markdown manifest summarizing prior Vitest and Playwright failures, classifying each as implementation vs harness, and noting patched vs remaining issues.
  • Record updated expectations for groups like transfer reconciliation, hash-chain reconciliation, CRM views, and governed transitions.
  • Capture remaining open issues (e.g., collection-plan execution ReferenceError) for follow-up work.
docs/test-failure-manifest.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on stabilizing failing Vitest + Playwright suites by updating fixtures/expectations to match current domain behavior, improving e2e selector robustness, and centralizing test harness/auth utilities (plus adding a failure triage manifest).

Changes:

  • Refactors Convex test harness registration (audit-log) and updates several Vitest expectations/fixtures to match current schemas/behavior.
  • Hardens multiple Playwright specs via helper utilities (RBAC role assertions, governed-transitions selectors, auth storage-state generation) and more resilient async flows.
  • Adds a detailed docs/test-failure-manifest.md to document historical failures and post-fix status.

Reviewed changes

Copilot reviewed 34 out of 35 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/test/convex/registerAuditLogComponent.ts Re-export audit-log component registrar from canonical Convex test helper.
src/test/convex/payments/helpers.ts Adds allowMixedMortgageObligations option to unblock mixed-obligation execution tests.
src/test/convex/engine/hash-chain-reconciliation.test.ts Updates hash-chain tests to seed richer audit journal entries + new canonical envelope expectations.
e2e/simulation.spec.ts Replaces brittle settlement branching with a deterministic outcome wait helper.
e2e/rbac/member.spec.ts Uses shared helper for role badge assertion.
e2e/rbac/helpers.ts Adds expectResolvedRole helper for role badge checks.
e2e/rbac/admin.spec.ts Uses shared helper for role badge assertion.
e2e/helpers/document-engine.ts Adds admin-auth bootstrapping helper + shared admin storage-state path.
e2e/helpers/auth-storage.ts Strengthens auth storage-state creation by verifying org switch has settled before saving.
e2e/governed-transitions.spec.ts Hardens navigation and entity selectors; reduces ambiguous locator usage.
e2e/document-engine/workflow.spec.ts Switches Document Engine workflow spec to use admin storageState.
e2e/document-engine/variables.spec.ts Uses admin storageState + bootstraps admin auth before running guarded-route tests.
e2e/document-engine/templates.spec.ts Uses admin storageState + bootstraps admin auth for setup/teardown flows.
e2e/document-engine/navigation.spec.ts Uses admin storageState + bootstraps admin auth before running guarded-route tests.
e2e/document-engine/library.spec.ts Uses admin storageState + bootstraps admin auth before running guarded-route tests.
e2e/document-engine/groups.spec.ts Uses admin storageState + bootstraps admin auth before running guarded-route tests.
e2e/document-engine/generate.spec.ts Uses admin storageState + bootstraps admin auth before running guarded-route tests.
e2e/document-engine/designer.spec.ts Uses admin storageState + bootstraps admin auth for setup/teardown flows.
e2e/demo-listings.spec.ts Replaces hard-coded listing slug/title with fixture-driven featured listing selection.
e2e/auth/protected-routes.spec.ts Updates /sign-in redirect assertion to AuthKit host pattern.
e2e/auth/onboarding.spec.ts Reworks onboarding auth to generate fresh per-test storage states and removes hard-coded fixture email usage.
e2e/auth/login.spec.ts Scopes profile email assertions to a specific “Email” card to avoid strict-mode collisions.
docs/test-failure-manifest.md Adds comprehensive failure triage/manifest and post-fix verification notes.
convex/test/registerAuditLogComponent.ts Switches to convex-audit-log/test’s official registrar.
convex/payments/webhooks/tests/vopayWebhook.test.ts Seeds missing transfer context fields (mortgage/obligation) for webhook tests.
convex/payments/webhooks/tests/eftVopayWebhook.test.ts Seeds required lender + accounts and fills lenderId for EFT VoPay tests.
convex/payments/transfers/tests/collectionAttemptReconciliation.integration.test.ts Updates expectations for transfer-owned cash receipt posting.
convex/payments/collectionPlan/tests/ruleContract.test.ts Updates rule-config tests to assert typed config behavior.
convex/payments/collectionPlan/tests/execution.test.ts Enables mixed-mortgage obligation seeding for the intended rejection-path test.
convex/payments/cashLedger/tests/transferReconciliation.test.ts Updates tests to assert escalation behavior (vs self-healing) and revises assertions.
convex/machines/tests/deal.integration.test.ts Adds fake-timer setup/teardown to prevent timer leakage/cascading harness failures.
convex/engine/machines/tests/collectionAttempt.test.ts Updates confirmed-state assertions to reflect recordSettlementObserved on FUNDS_SETTLED.
convex/crm/tests/viewEngine.test.ts Updates computed-field fieldDefId expectation.
convex/crm/tests/userSavedViews.test.ts Updates CRM auth expectations to match current admin-only contract.
.gitignore Ignores .tmp/ directory used by e2e onboarding auth-state generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread e2e/helpers/document-engine.ts
Comment thread e2e/auth/onboarding.spec.ts Outdated
Comment thread e2e/document-engine/workflow.spec.ts
Comment thread convex/payments/cashLedger/__tests__/transferReconciliation.test.ts Outdated
Comment thread convex/payments/cashLedger/__tests__/transferReconciliation.test.ts Outdated
Comment thread convex/payments/cashLedger/__tests__/transferReconciliation.test.ts Outdated
@Connorbelez Connorbelez force-pushed the 04-11-provider_managed_payment_schedule branch from 102424e to a99f4e2 Compare April 12, 2026 00:53
@Connorbelez Connorbelez force-pushed the test-fixes branch 3 times, most recently from ab2b98e to 3a5ca5e Compare April 12, 2026 03:14
@Connorbelez Connorbelez changed the base branch from 04-11-provider_managed_payment_schedule to graphite-base/393 April 13, 2026 16:48
This was referenced Apr 13, 2026
Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 14, 2026

Merge activity

  • Apr 14, 5:26 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 14, 5:28 PM UTC: Graphite couldn't merge this PR because it had merge conflicts.
  • Apr 14, 9:26 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 14, 9:27 PM UTC: @Connorbelez merged this pull request with Graphite.

@Connorbelez Connorbelez changed the base branch from graphite-base/393 to main April 14, 2026 17:26
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
convex/payments/transfers/queries.ts (1)

301-307: ⚠️ Potential issue | 🟠 Major

Close the getTransfersByPipeline auth bypass.

Line 306 adds a deal-level guard to getPipelineStatus, but the sibling getTransfersByPipeline query below is still .public() and returns the same legs for any pipelineId. That leaves the new restriction bypassable and leaks cross-tenant transfer metadata. Please either make getTransfersByPipeline internal or resolve the owning deal/mortgage and run the same access assertion before returning legs.

As per coding guidelines, Authorization (RBAC) must be wired into every query and mutation from day one via middleware chains.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/payments/transfers/queries.ts` around lines 301 - 307,
getTransfersByPipeline currently remains public and leaks transfer legs for any
pipelineId while getPipelineStatus enforces deal-level access; fix by wiring the
same authorization: either change getTransfersByPipeline from .public() to the
internal paymentQuery chain used by getPipelineStatus, or resolve the owning
deal/mortgage for the supplied pipelineId (lookup pipeline -> mortgage/deal) and
call assertDealAccess(ctx, dealId) before returning legs. Reference
getTransfersByPipeline, getPipelineStatus, and assertDealAccess when applying
the change so the query enforces RBAC consistently.
convex/payments/cashLedger/integrations.ts (1)

1387-1420: ⚠️ Potential issue | 🟠 Major

Don't treat corrected receipts as ambiguous originals.

postCashCorrectionForEntry() preserves attemptId/transferRequestId on replacement entries. If a CASH_RECEIVED is corrected with another CASH_RECEIVED, both rows satisfy this predicate and this branch now throws AMBIGUOUS_ORIGINAL_ENTRY, so a later payment/transfer reversal can no longer run against an otherwise valid history. Resolve the current live receipt from the correction/reversal ancestry before enforcing the single-match invariant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/payments/cashLedger/integrations.ts` around lines 1387 - 1420, The
current filter over allObligationEntries (used in cashReceivedMatches) can match
both an original CASH_RECEIVED and its corrected replacement because
postCashCorrectionForEntry preserves attemptId/transferRequestId; update the
logic to first resolve each candidate to its current live entry via the
correction/replacement ancestry (follow replacementId/previousEntryId chain or
use the same helper used by postCashCorrectionForEntry to get the canonical
entry) and then deduplicate by that canonical entry id before enforcing the
single-match invariant; keep throwing ORIGINAL_ENTRY_NOT_FOUND when none, but
only throw AMBIGUOUS_ORIGINAL_ENTRY when multiple distinct canonical
CASH_RECEIVED entries remain (include matchingEntryIds from those canonical
ids).
🧹 Nitpick comments (6)
convex/payments/transfers/providers/__tests__/registry.test.ts (1)

40-44: Add the missing failure-path test for pad_rotessa env gating.

You now cover the configured path; add one test for missing/empty ROTESSA_API_KEY to lock down registry behavior and prevent silent regressions.

Suggested test addition
 it('resolves "pad_rotessa" to RotessaTransferProvider when configured', () => {
 	vi.stubEnv("ROTESSA_API_KEY", "test_rotessa_api_key");
 	const provider = getTransferProvider("pad_rotessa");
 	expect(provider).toBeInstanceOf(RotessaTransferProvider);
 });
+
+it('throws for "pad_rotessa" when ROTESSA_API_KEY is unset', () => {
+	vi.stubEnv("ROTESSA_API_KEY", "");
+	expect(() => getTransferProvider("pad_rotessa")).toThrow(/rotessa|api key|configured/i);
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/payments/transfers/providers/__tests__/registry.test.ts` around lines
40 - 44, Add a test that verifies the registry rejects "pad_rotessa" when
ROTESSA_API_KEY is not set: stub the environment so ROTESSA_API_KEY is undefined
or empty (using vi.stubEnv), call getTransferProvider("pad_rotessa") and assert
the call fails (e.g., throws an Error or returns a clearly-failing result per
the registry's contract) instead of returning a RotessaTransferProvider;
reference getTransferProvider and RotessaTransferProvider in the new test to
ensure the env-gating behavior is locked down.
e2e/rbac/helpers.ts (1)

4-7: Scope the "Role" locator to the Session Context card for maintainability.

Currently, page.getByText("Role", { exact: true }) is page-global. While only one "Role" label exists in the demo/workos UI, scoping the selector to the explicit "Session Context" container makes the test more resilient and clearly documents the intended target.

Suggested refactor
 export async function expectResolvedRole(page: Page, role: string) {
-	const roleLabel = page.getByText("Role", { exact: true });
+	const sessionCard = page.getByText("Session Context", { exact: true }).locator("..");
+	const roleLabel = sessionCard.getByText("Role", { exact: true });
 	await expect(roleLabel).toBeVisible({ timeout: 10_000 });
 	await expect(roleLabel.locator("xpath=following-sibling::*[1]")).toHaveText(
 		role,
 		{ timeout: 10_000 }
 	);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/rbac/helpers.ts` around lines 4 - 7, The Role label locator is currently
page-global (page.getByText("Role", { exact: true })) and should be scoped to
the "Session Context" card to make the test resilient; update the selector so
you first locate the "Session Context" container (e.g., via getByText("Session
Context") or a container locator used in this file) and then call
getByText("Role", { exact: true }) on that container to produce roleLabel, then
keep the existing visibility/assertion and the following-sibling locator usage
(roleLabel.locator("xpath=following-sibling::*[1]")) unchanged so behavior is
the same but scoped.
e2e/governed-transitions.spec.ts (1)

74-76: Scope getEntityCard to entity-card containers to reduce selector collisions.

Current locator targets any matching button text, which can accidentally match nav/action buttons and cause flaky assertions as the page grows.

Refactor direction
 function getEntityCard(page: import("@playwright/test").Page, label: string) {
-	return page.locator("button").filter({ hasText: label }).first();
+	return page
+		.locator("[data-testid='entity-card']")
+		.filter({ has: page.getByText(label, { exact: true }) })
+		.first();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/governed-transitions.spec.ts` around lines 74 - 76, The getEntityCard
locator is too broad and matches any button with the given text; restrict it to
buttons inside the entity-card component to avoid collisions with nav/action
buttons. Update the getEntityCard function to first scope to the entity-card
container (e.g., locate the "entity-card" host/selector) and then find the
button with the given label within that container (still using .first() to keep
existing behavior); reference getEntityCard and the entity-card container when
making the change so the selector now targets only buttons inside entity-card
elements.
convex/engine/machines/__tests__/collectionAttempt.test.ts (1)

469-475: Clarify the FUNDS_SETTLED case name in this loop.

This branch now expects recordSettlementObserved, so the generated title confirmed ignores FUNDS_SETTLED is misleading. Consider splitting/renaming that case to reflect “same-state with side effect.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/engine/machines/__tests__/collectionAttempt.test.ts` around lines 469
- 475, The test branch checks event.type === "FUNDS_SETTLED" and expects actions
to include "recordSettlementObserved", so rename or split the test case that
currently reads something like "confirmed ignores FUNDS_SETTLED" into a clearer
description (e.g., "confirmed same-state emits recordSettlementObserved for
FUNDS_SETTLED") or split into two tests: one that asserts FUNDS_SETTLED yields
recordSettlementObserved and another that asserts all other events produce no
actions; update the test title(s) near the assertions referencing
"FUNDS_SETTLED" and "recordSettlementObserved" in collectionAttempt.test.ts
accordingly.
src/test/auth/permissions/permission-metadata-sync.test.ts (1)

52-95: Factor repeated orphan rationale into a shared constant.

The repeated "Protected in practice by admin:access wildcard..." string appears many times; extracting it into a constant will reduce copy/paste churn and typo drift.

♻️ Suggested refactor
+const ADMIN_WILDCARD_ORPHAN_REASON =
+	"Protected in practice by admin:access wildcard; no longer explicitly assigned in ROLE_PERMISSIONS";
+
 const KNOWN_ORPHANS: Record<string, string> = {
 	"onboarding:manage":
 		"Reserved for future onboarding admin workflow; metadata kept for UI completeness",
-	"onboarding:review":
-		"Protected in practice by admin:access wildcard; no longer explicitly assigned in ROLE_PERMISSIONS",
-	"role:assign":
-		"Protected in practice by admin:access wildcard; no longer explicitly assigned in ROLE_PERMISSIONS",
+	"onboarding:review": ADMIN_WILDCARD_ORPHAN_REASON,
+	"role:assign": ADMIN_WILDCARD_ORPHAN_REASON,
 	// ...
 };

Based on learnings: “DRY: Don't Repeat Yourself - abstract repeated code into reusable functions or modules”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/auth/permissions/permission-metadata-sync.test.ts` around lines 52 -
95, The large repeated rationale string ("Protected in practice by admin:access
wildcard; ...") should be extracted into a single constant (e.g.,
PROTECTED_BY_ADMIN_RATIONALE) in the test module and reused for each permission
entry instead of repeating the literal; update permission-metadata-sync.test.ts
to declare the constant near the top and replace all occurrences in the
permission map (references around the ROLE_PERMISSIONS/permission entries) with
that constant to reduce duplication and prevent typos.
convex/payments/cashLedger/__tests__/transferReconciliation.test.ts (1)

640-695: Align the "to SUSPENSE" test names with what the assertions now prove.

Both cases still describe a SUSPENSE/LENDER_PAYABLE posting, but the updated checks now require getSuspenseEscalatedEntryForTransfer(...) to return null. Rename the tests/comments or restore the ledger assertion if suspense posting is still part of the contract.

Also applies to: 786-835

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/payments/cashLedger/__tests__/transferReconciliation.test.ts` around
lines 640 - 695, The test name and comments imply a SUSPENSE/LENDER_PAYABLE
ledger posting but the assertions (using getSuspenseEscalatedEntryForTransfer
and expecting null) prove no suspense entry is created; update the test and its
duplicate (the other case around transferReconciliation.test lines ~786-835) to
reflect the actual behavior by renaming the test descriptions/comments to state
"escalates to SUSPENSE without creating a suspense journal entry" or "escalates
without suspense posting", or if the contract should still create a suspense
entry, restore the ledger assertion to expect a non-null suspense entry; locate
the tests referencing transferReconciliationCron.retriggerTransferConfirmation
and getSuspenseEscalatedEntryForTransfer to make the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@convex/auth/resourceChecks.ts`:
- Around line 344-369: canAccessBorrowerEntity currently grants access if the
viewer can access any mortgage for that borrower, which lets a viewer with
access to mortgage A wrongly pass checks for borrower-linked resources of
mortgage B; change canAccessBorrowerEntity to accept an optional resource
context id (e.g., mortgageId or obligation/transfer id) and only treat
third-party access as valid when a found mortgageBorrowers link matches that
context before calling canAccessMortgage; keep the existing short-circuits for
viewer.isFairLendAdmin and self-access via getBorrowerByAuthId, but when
iterating ctx.db.query("mortgageBorrowers").withIndex("by_borrower") only
consider links where link.mortgageId === providedContextId (and then call
canAccessMortgage for that matching mortgageId).

In `@convex/engine/commands.ts`:
- Around line 146-159: The code currently hardcodes requestedByActorId,
requestedByActorType, and triggerSource as admin values when calling
runManualInboundCollectionForObligation; instead derive these metadata fields
from ctx.viewer (use ctx.viewer.authId for requestedByActorId and a
viewer-sourced type/role for requestedByActorType) and set triggerSource based
on the actual caller context (e.g., "admin_manual" only if ctx.viewer indicates
an admin, otherwise "manual" or another appropriate value), or alternatively
tighten the endpoint to require an explicit admin-only guard; update the call
site that constructs the manualSettlement payload and the requestedBy* fields to
use ctx.viewer-derived values rather than the hardcoded "admin" literals.

In `@convex/payments/collectionPlan/__tests__/admin.test.ts`:
- Around line 184-209: The audit assertion is too weak because
Array.isArray(attemptAuditEvents) passes for an empty array; update the test
(around the attemptAuditEvents result from auditLog.queryByResource) to assert a
non-zero count or that a specific expected audit action exists (e.g.,
expect(attemptAuditEvents.length).toBeGreaterThan(0) or check for an event with
the expected action/type in attemptAuditEvents), ensuring the
auditLog.queryByResource call for resourceType "collectionAttempts" and
resourceId `${execution.collectionAttemptId}` actually produced entries.

In `@convex/payments/collectionPlan/manualCollection.ts`:
- Around line 83-119: The current non-atomic check in
findExistingManualPlanEntryId (which iterates MANUAL_COLLECTION_ENTRY_STATUSES
and calls internal.payments.collectionPlan.queries.getPlanEntriesByStatus)
allows race conditions where concurrent requests both miss and insert duplicate
collectionPlanEntries; replace this by moving the existence check and insert
into a single atomic indexed mutation keyed by executionIdempotencyKey (e.g.,
add a unique index on collectionPlanEntries.executionIdempotencyKey and
implement a mutation like createOrGetCollectionPlanEntry that upserts or returns
the existing document id), update callers (createEntry / executePlanEntry) to
call that mutation and return the canonical entry _id, and remove the
multi-bucket scan in findExistingManualPlanEntryId so idempotency is enforced by
the DB-backed mutation.

In `@convex/payments/collectionPlan/workout.ts`:
- Around line 71-97: The write handlers createWorkoutPlan, activateWorkoutPlan,
completeWorkoutPlan, and cancelWorkoutPlan currently call their mutation
implementations without resource checks; update each handler to call the
appropriate guard (assertMortgageAccess for handlers operating on a mortgage id
and assertWorkoutPlanAccess for handlers operating on a workoutPlanId) with the
same ctx and ctx.viewer before invoking the mutation logic (ensure you pass the
correct id param into the assert call), so authorization enforced by
canAccessWorkoutPlan/canAccessMortgage is applied to all mutations invoked by
paymentMutation.

In `@convex/payments/webhooks/__tests__/stripeWebhook.test.ts`:
- Around line 411-447: The test currently only exercises the deferred handler
processUnsupportedStripeWebhook (it pre-seeds a webhookEvents row and calls that
function directly) while claiming to test the "persistence bridge"; update the
test to either (A) rename the describe/it to clearly state it tests the deferred
handler only, or (B) add a companion end-to-end test that invokes the actual
entrypoint stripeWebhook (or the code path that calls persistStripeWebhook and
ctx.scheduler.runAfter) so the full persistence + scheduling + HTTP handling
flow is covered; reference processUnsupportedStripeWebhook,
persistStripeWebhook, ctx.scheduler.runAfter and stripeWebhook when adding or
renaming tests so the intended unit under test is unambiguous.

In `@convex/payments/webhooks/stripe.ts`:
- Around line 153-177: The catch block in
scheduleUnsupportedStripeWebhookProcessing currently calls
markTransferWebhookFailed and if that throws it will bubble up and mask the
original scheduler failure; wrap the markTransferWebhookFailed call in its own
try/catch so the status update is best-effort (log any error from
markTransferWebhookFailed but do not rethrow), and always return the original {
ok: false, error: message } from scheduleUnsupportedStripeWebhookProcessing
after attempting the status update; reference ctx.scheduler.runAfter,
internal.payments.webhooks.stripe.processUnsupportedStripeWebhook, and
markTransferWebhookFailed to locate and change the code.
- Line 4: The exported webhook action processUnsupportedStripeWebhook is created
with raw internalAction(...) which violates the project's fluent-convex pattern;
replace its export to use the convex.action() builder by wiring the existing
handler function into
convex.action().handler(processUnsupportedStripeWebhook).internal() (or inline
the handler into .handler(...)) so the action follows the fluent chain; apply
the same replacement to other webhook modules (rotessaPad.ts, verification.ts,
legacyReversal.ts) to keep exports consistent.

In `@e2e/auth/onboarding.spec.ts`:
- Around line 28-46: createFreshStorageState and openOnboardingPage can leak
browser contexts and temp storage files when intermediate steps throw; wrap
creation and usage of bootstrapContext, test context/page and the
storageStatePath in try/finally blocks so that bootstrapContext.close() and test
context.close() are always called and the temporary file in
onboardingAuthStateDir (storageStatePath) is unlinked in the finally.
Specifically, update createFreshStorageState to create bootstrapContext,
bootstrapPage, and storageStatePath then call createAuthStorageState inside a
try and ensure bootstrapContext.close() in finally; similarly update
openOnboardingPage to wrap creation of the test context/page, page.goto and
visibility checks in try/finally so testContext.close() always runs and remove
the temp storageStatePath after use.

In `@e2e/helpers/document-engine.ts`:
- Around line 16-17: ADMIN_STORAGE_STATE being a fixed filename causes race
conditions when tests run in parallel; replace the shared constant with a
per-test unique storage path (e.g., build the filename using the orgId and a
random UUID like `storage-${orgId}-${randomUUID()}.json`) or change the write
logic to perform atomic writes (write to a temp file then rename) around
functions that read/write the storage state (references: ADMIN_STORAGE_STATE,
any helper functions that call existsSync()/writeFile()/readFile()) so each
worker uses its own file or performs atomic replace to avoid concurrent
read/write races.

In `@src/lib/auth.ts`:
- Around line 25-28: The hasPermission function currently treats
ADMIN_ACCESS_PERMISSION as a global override which lets callers like
canAccessAdminPath and requireFairLendAdmin escape staff-boundary checks; change
hasPermission to accept an options flag (e.g., allowAdminOverride: boolean =
true) and only treat ADMIN_ACCESS_PERMISSION as an override when that flag is
true, defaulting to true to preserve existing behavior; then update
boundary-sensitive callers (canAccessAdminPath, requireFairLendAdmin, and any
underwriter checks) to call hasPermission(..., { allowAdminOverride: false }) so
admin:access does not implicitly grant staff-boundary permissions.

In `@src/routes/e2e/session.tsx`:
- Around line 32-54: The async IIFE can throw when calling refresh(),
getAccessToken(), or decodeAccessToken, leaving tokenSnapshot null and causing
/e2e/session to hang; wrap the token acquisition and decode steps (the calls to
refresh(), getAccessToken(), and decodeAccessToken(token)) in a try/catch, and
in the catch ensure you call setTokenSnapshot with the same "logged-out" default
object (organizationId: null, permissions: [], role: null) and return,
optionally logging the error, so the route can terminate even on token
acquisition/decoding failures; keep the existing cancelled checks around
setTokenSnapshot.

---

Outside diff comments:
In `@convex/payments/cashLedger/integrations.ts`:
- Around line 1387-1420: The current filter over allObligationEntries (used in
cashReceivedMatches) can match both an original CASH_RECEIVED and its corrected
replacement because postCashCorrectionForEntry preserves
attemptId/transferRequestId; update the logic to first resolve each candidate to
its current live entry via the correction/replacement ancestry (follow
replacementId/previousEntryId chain or use the same helper used by
postCashCorrectionForEntry to get the canonical entry) and then deduplicate by
that canonical entry id before enforcing the single-match invariant; keep
throwing ORIGINAL_ENTRY_NOT_FOUND when none, but only throw
AMBIGUOUS_ORIGINAL_ENTRY when multiple distinct canonical CASH_RECEIVED entries
remain (include matchingEntryIds from those canonical ids).

In `@convex/payments/transfers/queries.ts`:
- Around line 301-307: getTransfersByPipeline currently remains public and leaks
transfer legs for any pipelineId while getPipelineStatus enforces deal-level
access; fix by wiring the same authorization: either change
getTransfersByPipeline from .public() to the internal paymentQuery chain used by
getPipelineStatus, or resolve the owning deal/mortgage for the supplied
pipelineId (lookup pipeline -> mortgage/deal) and call assertDealAccess(ctx,
dealId) before returning legs. Reference getTransfersByPipeline,
getPipelineStatus, and assertDealAccess when applying the change so the query
enforces RBAC consistently.

---

Nitpick comments:
In `@convex/engine/machines/__tests__/collectionAttempt.test.ts`:
- Around line 469-475: The test branch checks event.type === "FUNDS_SETTLED" and
expects actions to include "recordSettlementObserved", so rename or split the
test case that currently reads something like "confirmed ignores FUNDS_SETTLED"
into a clearer description (e.g., "confirmed same-state emits
recordSettlementObserved for FUNDS_SETTLED") or split into two tests: one that
asserts FUNDS_SETTLED yields recordSettlementObserved and another that asserts
all other events produce no actions; update the test title(s) near the
assertions referencing "FUNDS_SETTLED" and "recordSettlementObserved" in
collectionAttempt.test.ts accordingly.

In `@convex/payments/cashLedger/__tests__/transferReconciliation.test.ts`:
- Around line 640-695: The test name and comments imply a
SUSPENSE/LENDER_PAYABLE ledger posting but the assertions (using
getSuspenseEscalatedEntryForTransfer and expecting null) prove no suspense entry
is created; update the test and its duplicate (the other case around
transferReconciliation.test lines ~786-835) to reflect the actual behavior by
renaming the test descriptions/comments to state "escalates to SUSPENSE without
creating a suspense journal entry" or "escalates without suspense posting", or
if the contract should still create a suspense entry, restore the ledger
assertion to expect a non-null suspense entry; locate the tests referencing
transferReconciliationCron.retriggerTransferConfirmation and
getSuspenseEscalatedEntryForTransfer to make the change.

In `@convex/payments/transfers/providers/__tests__/registry.test.ts`:
- Around line 40-44: Add a test that verifies the registry rejects "pad_rotessa"
when ROTESSA_API_KEY is not set: stub the environment so ROTESSA_API_KEY is
undefined or empty (using vi.stubEnv), call getTransferProvider("pad_rotessa")
and assert the call fails (e.g., throws an Error or returns a clearly-failing
result per the registry's contract) instead of returning a
RotessaTransferProvider; reference getTransferProvider and
RotessaTransferProvider in the new test to ensure the env-gating behavior is
locked down.

In `@e2e/governed-transitions.spec.ts`:
- Around line 74-76: The getEntityCard locator is too broad and matches any
button with the given text; restrict it to buttons inside the entity-card
component to avoid collisions with nav/action buttons. Update the getEntityCard
function to first scope to the entity-card container (e.g., locate the
"entity-card" host/selector) and then find the button with the given label
within that container (still using .first() to keep existing behavior);
reference getEntityCard and the entity-card container when making the change so
the selector now targets only buttons inside entity-card elements.

In `@e2e/rbac/helpers.ts`:
- Around line 4-7: The Role label locator is currently page-global
(page.getByText("Role", { exact: true })) and should be scoped to the "Session
Context" card to make the test resilient; update the selector so you first
locate the "Session Context" container (e.g., via getByText("Session Context")
or a container locator used in this file) and then call getByText("Role", {
exact: true }) on that container to produce roleLabel, then keep the existing
visibility/assertion and the following-sibling locator usage
(roleLabel.locator("xpath=following-sibling::*[1]")) unchanged so behavior is
the same but scoped.

In `@src/test/auth/permissions/permission-metadata-sync.test.ts`:
- Around line 52-95: The large repeated rationale string ("Protected in practice
by admin:access wildcard; ...") should be extracted into a single constant
(e.g., PROTECTED_BY_ADMIN_RATIONALE) in the test module and reused for each
permission entry instead of repeating the literal; update
permission-metadata-sync.test.ts to declare the constant near the top and
replace all occurrences in the permission map (references around the
ROLE_PERMISSIONS/permission entries) with that constant to reduce duplication
and prevent typos.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a859562-b568-4969-90ec-252b7c681b26

📥 Commits

Reviewing files that changed from the base of the PR and between 3a370fe and 7d28837.

📒 Files selected for processing (74)
  • .gitignore
  • AGENTS.md
  • CLAUDE.md
  • convex/auth/__tests__/resourceChecks.test.ts
  • convex/auth/resourceChecks.ts
  • convex/crm/__tests__/userSavedViews.test.ts
  • convex/crm/__tests__/viewEngine.test.ts
  • convex/demo/simulation.ts
  • convex/engine/commands.ts
  • convex/engine/effects/__tests__/transfer.test.ts
  • convex/engine/effects/collectionAttempt.ts
  • convex/engine/effects/obligationPayment.ts
  • convex/engine/machines/__tests__/collectionAttempt.test.ts
  • convex/fluent.ts
  • convex/machines/__tests__/deal.integration.test.ts
  • convex/payments/bankAccounts/queries.ts
  • convex/payments/cashLedger/__tests__/transferReconciliation.test.ts
  • convex/payments/cashLedger/integrations.ts
  • convex/payments/cashLedger/queries.ts
  • convex/payments/collectionPlan/__tests__/admin.test.ts
  • convex/payments/collectionPlan/__tests__/execution.test.ts
  • convex/payments/collectionPlan/__tests__/ruleContract.test.ts
  • convex/payments/collectionPlan/execution.ts
  • convex/payments/collectionPlan/executionContract.ts
  • convex/payments/collectionPlan/initialScheduling.ts
  • convex/payments/collectionPlan/manualCollection.ts
  • convex/payments/collectionPlan/mutations.ts
  • convex/payments/collectionPlan/workout.ts
  • convex/payments/transfers/__tests__/collectionAttemptReconciliation.integration.test.ts
  • convex/payments/transfers/collectionAttemptReconciliation.ts
  • convex/payments/transfers/providers/__tests__/registry.test.ts
  • convex/payments/transfers/queries.ts
  • convex/payments/webhooks/__tests__/eftVopayWebhook.test.ts
  • convex/payments/webhooks/__tests__/reversalIntegration.test.ts
  • convex/payments/webhooks/__tests__/stripeWebhook.test.ts
  • convex/payments/webhooks/__tests__/vopayWebhook.test.ts
  • convex/payments/webhooks/stripe.ts
  • convex/test/registerAuditLogComponent.ts
  • docs/test-failure-manifest.md
  • e2e/auth/login.spec.ts
  • e2e/auth/onboarding.spec.ts
  • e2e/auth/protected-routes.spec.ts
  • e2e/demo-listings.spec.ts
  • e2e/document-engine/designer.spec.ts
  • e2e/document-engine/generate.spec.ts
  • e2e/document-engine/groups.spec.ts
  • e2e/document-engine/library.spec.ts
  • e2e/document-engine/navigation.spec.ts
  • e2e/document-engine/templates.spec.ts
  • e2e/document-engine/variables.spec.ts
  • e2e/document-engine/workflow.spec.ts
  • e2e/governed-transitions.spec.ts
  • e2e/helpers/auth-storage.ts
  • e2e/helpers/document-engine.ts
  • e2e/rbac/admin.spec.ts
  • e2e/rbac/helpers.ts
  • e2e/rbac/member.spec.ts
  • e2e/simulation.spec.ts
  • playwright.config.ts
  • src/lib/auth.ts
  • src/routeTree.gen.ts
  • src/routes/_authenticated/authenticated.tsx
  • src/routes/demo/convex.tsx
  • src/routes/demo/simulation.tsx
  • src/routes/e2e/session.tsx
  • src/test/auth/chains/role-chains.test.ts
  • src/test/auth/middleware/requirePermission.test.ts
  • src/test/auth/permissions.ts
  • src/test/auth/permissions/new-permissions.test.ts
  • src/test/auth/permissions/permission-metadata-sync.test.ts
  • src/test/auth/route-guards.test.ts
  • src/test/convex/engine/hash-chain-reconciliation.test.ts
  • src/test/convex/payments/helpers.ts
  • src/test/convex/registerAuditLogComponent.ts

Comment thread convex/auth/resourceChecks.ts Outdated
Comment thread convex/engine/commands.ts
Comment thread convex/payments/collectionPlan/__tests__/admin.test.ts
Comment thread convex/payments/collectionPlan/manualCollection.ts Outdated
Comment thread convex/payments/collectionPlan/workout.ts
Comment thread convex/payments/webhooks/stripe.ts
Comment thread e2e/auth/onboarding.spec.ts Outdated
Comment thread e2e/helpers/document-engine.ts
Comment thread src/lib/auth.ts
Comment thread src/routes/e2e/session.tsx
@Connorbelez Connorbelez merged commit b8e26d6 into main Apr 14, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants