Skip to content

feat(eng-246): org-scoped audit and denormalized orgId on domain tables#341

Merged
Connorbelez merged 2 commits intomainfrom
03-29-eng-246
Mar 29, 2026
Merged

feat(eng-246): org-scoped audit and denormalized orgId on domain tables#341
Connorbelez merged 2 commits intomainfrom
03-29-eng-246

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Mar 29, 2026

  • Add convex/lib/orgScope for broker/mortgage/transfer org resolution and
    audit journal organizationId extraction.
  • Extend schema with optional orgId on brokers, borrowers, lenders, mortgages,
    obligations, deals, dispersalEntries, transferRequests; organizationId on
    auditJournal; rename broker brokerageOrgId to orgId with org indexes.
  • Thread org through seeds, simulation, obligations, dispersal, transfers,
    onboarding, and transition audit entries.
  • Fix transfer internal-action circular typing (explicit return types);
    use legNumberValidator on principal return.
  • Split root vs Convex typecheck (exclude convex/ from root tsconfig).

Summary by CodeRabbit

Release Notes

  • Chores
    • Improved infrastructure for data organization and indexing to enhance query performance and audit tracking capabilities.
    • Strengthened type safety and validation across payment processing and transfer workflows.
    • Updated TypeScript configuration to ensure comprehensive type checking across the codebase.

- Add convex/lib/orgScope for broker/mortgage/transfer org resolution and
  audit journal organizationId extraction.
- Extend schema with optional orgId on brokers, borrowers, lenders, mortgages,
  obligations, deals, dispersalEntries, transferRequests; organizationId on
  auditJournal; rename broker brokerageOrgId to orgId with org indexes.
- Thread org through seeds, simulation, obligations, dispersal, transfers,
  onboarding, and transition audit entries.
- Fix transfer internal-action circular typing (explicit return types);
  use legNumberValidator on principal return.
- Split root vs Convex typecheck (exclude convex/ from root tsconfig).

Made-with: Cursor
@linear
Copy link
Copy Markdown

linear bot commented Mar 29, 2026

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.

Sorry @Connorbelez, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

Warning

Rate limit exceeded

@Connorbelez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 45 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 4 minutes and 45 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: 1dfeb84d-c880-4450-96ef-0304df9efec6

📥 Commits

Reviewing files that changed from the base of the PR and between 07acb40 and 3e52c4c.

📒 Files selected for processing (9)
  • convex/brokers/migrations.ts
  • convex/dispersal/disbursementBridge.ts
  • convex/engine/effects/collectionAttempt.ts
  • convex/lib/__tests__/orgScope.transferResolution.test.ts
  • convex/lib/orgScope.ts
  • convex/payments/obligations/createCorrectiveObligation.ts
  • convex/payments/transfers/mutations.ts
  • convex/seed/seedBroker.ts
  • convex/seed/seedMortgage.ts
📝 Walkthrough

Walkthrough

This PR introduces organization-level scoping across the data model and mutation flows. It adds optional orgId fields to multiple tables (borrowers, lenders, mortgages, obligations, deals, dispersalEntries, transferRequests), replaces brokerageOrgId with orgId in brokers, and adds organizationId to audit journal entries. Supporting utilities derive org scope from related entities, and seeding/mutation logic now populates orgId on inserted records and propagates organizationId through audit trails.

Changes

Cohort / File(s) Summary
Schema & Audit Types
convex/schema.ts, convex/engine/types.ts
Added optional orgId fields to borrowers, lenders, mortgages, obligations, deals, dispersalEntries, transferRequests; replaced brokers.brokerageOrgId with brokers.orgId; added organizationId to auditJournal; introduced org-based and org+status indexes for efficient querying.
Org Scope Utilities
convex/lib/orgScope.ts
New module exporting functions to extract audit organization IDs from entity documents, enforce org presence on brokers, and resolve org IDs from related mortgage/obligation/transfer records via prioritized reference chains.
Audit & Transition Logic
convex/engine/transition.ts, convex/engine/effects/transfer.ts, convex/engine/effects/collectionAttempt.ts
Updated state transition and effect handlers to derive organizationId from entity documents and include it in all audit journal entries; added new field propagation without altering control flow.
Seed & Simulation
convex/demo/simulation.ts, convex/seed/seedBorrower.ts, convex/seed/seedBroker.ts, convex/seed/seedDeal.ts, convex/seed/seedLender.ts, convex/seed/seedMortgage.ts, convex/seed/seedObligation.ts, convex/seed/seedOnboardingRequest.ts, convex/seed/seedHelpers.ts
Extended seed/simulation flows to look up and populate orgId on inserted borrowers, lenders, mortgages, obligations, deals; added organizationId to creation and synthetic journal entries; updated helper interfaces to accept optional organizationId.
Dispersal & Obligation Mutations
convex/dispersal/createDispersalEntries.ts, convex/dispersal/disbursementBridge.ts, convex/obligations/mutations.ts, convex/payments/obligations/createCorrectiveObligation.ts, convex/payments/obligations/generateImpl.ts
Added orgId derivation and persistence to dispersal entries, transfer requests, and obligation records; propagated organizationId to audit journal entries for relevant state changes.
Transfer Mutations & Actions
convex/payments/transfers/mutations.ts, convex/payments/transfers/depositCollection.ts, convex/payments/transfers/principalReturn.ts, convex/onboarding/mutations.ts
Added orgId computation and persistence to transfer request creation flows; tightened return type annotations on handlers; refactored early-return structures for collectCommitmentDepositAdmin and returnInvestorPrincipal; added input validation refinements for legNumber; propagated organizationId to audit journal entries.
Configuration & Documentation
package.json, tsconfig.json, specs/ENG-20/chunks/.../context.md, specs/ENG-20/chunks/.../status.md
Updated typecheck npm script to run both default and Convex-specific TypeScript compilation; excluded convex/** from root tsconfig; updated spec docs to reflect brokers.orgId field name change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~40 minutes

Possibly related issues

Possibly related PRs

  • ENG-13 #59: Both PRs modify the audit journal and AuditJournalEntry type-level schema structure, adding fields to support audit metadata.
  • ENG-174: Payout hold period + Biome updates #272: Both PRs touch the createDispersalEntries.ts file and dispersalEntries schema (current PR adds orgId, retrieved PR adds payment/payout fields).
  • eng-165 #271: Both PRs modify transferRequests schema and creation flows to add new fields and related insertion/propagation logic.

Poem

🐰 Organizations now have their own secret gardens of data,
Where borrowers, lenders, and mortgages align with their keepers,
Audit trails glow with org-scoped breadcrumbs,
And every seed knows exactly which warren it belongs to!
Thus order blooms from chaos — hop along, scoped systems! 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% 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 accurately and concisely describes the main change: adding organization-scoped audit capabilities and denormalized orgId fields across domain tables, which aligns with the comprehensive set of changes throughout the codebase.

✏️ 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 03-29-eng-246

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.

Copy link
Copy Markdown
Owner Author

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

@Connorbelez Connorbelez marked this pull request as ready for review March 29, 2026 16:38
Copilot AI review requested due to automatic review settings March 29, 2026 16:38
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.

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

Adds organization scoping across domain tables and audit journaling by denormalizing orgId (WorkOS org id) onto core Convex tables and threading organizationId through GT/audit entry creation and seed/simulation flows.

Changes:

  • Introduces convex/lib/orgScope helpers for deriving org scope (especially for transfers) and for extracting organizationId for audit rows.
  • Extends Convex schema with optional orgId on multiple domain tables + organizationId on auditJournal, including new org-based indexes.
  • Threads org scope through seeders, simulations, transfer creation, obligation generation/correctives, onboarding, and GT transition audit entries; splits root vs Convex TS typechecking.

Reviewed changes

Copilot reviewed 27 out of 29 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tsconfig.json Excludes convex/** from root TS project to split typechecking concerns.
package.json Runs root typecheck plus dedicated Convex project typecheck.
specs/ENG-20/chunks/chunk-01-foundation-and-profiles/status.md Updates spec note to reflect brokers.orgId rename.
specs/ENG-20/chunks/chunk-01-foundation-and-profiles/context.md Updates schema/spec references from brokerageOrgId to orgId.
convex/seed/seedOnboardingRequest.ts Adds organizationId to onboarding audit journal seeding.
convex/seed/seedObligation.ts Denormalizes obligation orgId from mortgage; threads organizationId into seeded audit entries.
convex/seed/seedMortgage.ts Denormalizes mortgage orgId from broker; threads organizationId into seeded audit entries.
convex/seed/seedLender.ts Denormalizes lender orgId from broker; threads organizationId into seeded audit entries.
convex/seed/seedHelpers.ts Extends seed journal helpers to accept/persist organizationId.
convex/seed/seedDeal.ts Denormalizes deal orgId from mortgage; threads organizationId into seeded audit entries.
convex/seed/seedBroker.ts Renames broker brokerageOrgIdorgId; adds organizationId to audit seed entry payloads.
convex/seed/seedBorrower.ts Sets seeded borrower orgId and corresponding audit organizationId.
convex/schema.ts Adds optional orgId to multiple tables + org indexes; adds auditJournal.organizationId + org/time index.
convex/payments/transfers/principalReturn.ts Fixes typing/circularity via explicit return type and uses legNumberValidator.
convex/payments/transfers/mutations.ts Derives/persists transferRequests.orgId via orgIdForTransferRequest; adds explicit return types.
convex/payments/transfers/depositCollection.ts Fixes typing/circularity via explicit return type.
convex/payments/obligations/generateImpl.ts Denormalizes generated obligations’ orgId from the mortgage.
convex/payments/obligations/createCorrectiveObligation.ts Copies orgId to corrective obligations and sets audit organizationId.
convex/onboarding/mutations.ts Adds organizationId to onboarding request creation audit journal entry.
convex/obligations/mutations.ts Denormalizes obligation orgId from mortgage; sets audit organizationId.
convex/lib/orgScope.ts New helper module for org resolution and audit org extraction.
convex/engine/types.ts Adds organizationId to AuditJournalEntry interface.
convex/engine/transition.ts Extracts organizationId from entity documents and includes it in GT audit journal entries.
convex/engine/effects/transfer.ts Adds organizationId to transfer-related audit journal entries.
convex/engine/effects/collectionAttempt.ts Persists orgId on bridged transferRequests.
convex/dispersal/disbursementBridge.ts Persists orgId on transferRequests created by the bridge.
convex/dispersal/createDispersalEntries.ts Persists dispersal entry orgId from the mortgage.
convex/demo/simulation.ts Threads org scope through simulation-created brokers/borrowers/lenders/mortgages.
convex/_generated/api.d.ts Updates generated API typings to include new/updated modules.

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

Comment thread convex/seed/seedMortgage.ts
Comment thread convex/seed/seedBroker.ts
Comment thread convex/payments/obligations/createCorrectiveObligation.ts Outdated
Comment thread convex/payments/obligations/createCorrectiveObligation.ts Outdated
Comment thread convex/payments/transfers/mutations.ts
Comment thread convex/engine/effects/collectionAttempt.ts Outdated
Comment thread convex/dispersal/disbursementBridge.ts Outdated
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: 3

Caution

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

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

539-552: ⚠️ Potential issue | 🟠 Major

Only make the retry-safe fast path for already-progressed states.

This branch now returns { success: true } for any status other than "initiated", which means "failed" or "cancelled" transfers are reported as a successful no-op. That is especially risky because the known orchestrators in convex/payments/transfers/depositCollection.ts:79-92 and convex/payments/transfers/principalReturn.ts:86-100 currently ignore the returned TransitionResult and would still bubble up { transferId } as if initiation had succeeded.

Suggested patch
-		if (transfer.status !== "initiated") {
+		if (
+			transfer.status === "pending" ||
+			transfer.status === "processing" ||
+			transfer.status === "confirmed"
+		) {
 			console.info(
 				`[initiateTransferInternal] Transfer ${args.transferId} already in "${transfer.status}" — skipping initiation (retry-safe).`
 			);
 			return {
 				success: true,
 				previousState: transfer.status,
 				newState: transfer.status,
 				reason: "already_initiated_or_beyond",
 			};
 		}
+		if (transfer.status !== "initiated") {
+			throw new ConvexError(
+				`Transfer must be in "initiated" status to initiate, currently: "${transfer.status}"`
+			);
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/payments/transfers/mutations.ts` around lines 539 - 552, The current
retry-safe fast path in initiateTransferInternal returns success:true for any
transfer.status !== "initiated", which incorrectly treats "failed" or
"cancelled" as successful; change the condition to only short-circuit for
explicitly acceptable progressed states (e.g., ["pending","confirmed"]) and
return the existing success:true/previousState/newState only for those states;
for "failed" or "cancelled" return a failure result (success:false with a clear
reason) or throw so callers (like
createDealClosingPipeline/createAndInitiateLeg2) don't treat failed/cancelled
transfers as successful no-ops.
🧹 Nitpick comments (2)
convex/seed/seedDeal.ts (1)

219-222: Redundant mortgage lookup.

The mortgage was already validated in resolveMortgageIds (lines 131-136), so mortgageId is guaranteed to exist. This second ctx.db.get adds an unnecessary read.

Consider fetching mortgages once during resolution and passing the documents directly, or removing this check since the ID pool is pre-validated.

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

In `@convex/seed/seedDeal.ts` around lines 219 - 222, Remove the redundant
database lookup for mortgageId in seedDeal.ts: since resolveMortgageIds already
validates existence, eliminate the ctx.db.get(mortgageId) and the subsequent
mortgageRow null check/ConvexError throw; instead use the validated mortgage IDs
(or pass the pre-fetched mortgage documents from resolveMortgageIds into
createDeal if available) so seedDeal no longer performs an extra ctx.db.get for
mortgageId.
convex/seed/seedObligation.ts (1)

229-234: Redundant mortgage lookup.

The mortgage document is already fetched at line 182 into the mortgage variable for the same pair.mortgageId. This second lookup (mortgageRow) duplicates that read.

♻️ Use the existing mortgage variable
-				const mortgageRow = await ctx.db.get(pair.mortgageId);
-				if (!mortgageRow) {
-					throw new ConvexError(
-						`seedObligation: mortgage not found ${pair.mortgageId}`
-					);
-				}
-
 				const obligationId = await ctx.db.insert("obligations", {
-					orgId: mortgageRow.orgId,
+					orgId: mortgage.orgId,

Then update lines 258 and 275 to use mortgage.orgId instead of mortgageRow.orgId.

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

In `@convex/seed/seedObligation.ts` around lines 229 - 234, Remove the redundant
DB read that assigns mortgageRow by reusing the already-fetched mortgage
(fetched earlier for pair.mortgageId); delete the await ctx.db.get(...) that
creates mortgageRow, replace any uses of mortgageRow.orgId with mortgage.orgId
(including the occurrences noted around where lines ~258 and ~275 reference
orgId), and ensure any null-checks rely on the existing mortgage variable
instead of mortgageRow to avoid duplicate reads.
🤖 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/lib/orgScope.ts`:
- Around line 26-31: orgIdFromMortgageId currently returns mortgage?.orgId and
can miss legacy scoping; update it to fall back: if mortgage.orgId is empty,
check mortgage.brokerOfRecordId and, if present, fetch that broker (ctx.db.get
on the broker id) and return broker.orgId if set; if still empty, iterate
mortgage.lenders (if present), for each lender fetch the lender row (ctx.db.get)
and then fetch that lender's broker via lender.brokerId to return its orgId when
found; otherwise return undefined. Apply the same fallback logic to the other
orgId-resolver function in this module so both mortgage-based lookups resolve
via broker relationships when orgId is missing.

In `@convex/payments/transfers/mutations.ts`:
- Around line 857-858: The retry path is copying transfer.orgId directly when
inserting a new transferRequest, which can leave retries unscoped if the
original row lacked orgId; update the retry flow to recompute scope using
orgIdForTransferRequest(...) instead of reusing transfer.orgId—find the code
that calls ctx.db.insert("transferRequests", { orgId: transfer.orgId, ... }) in
the retryTransfer logic and replace the orgId value with the result of
orgIdForTransferRequest(...) (pass the same transfer/related identifiers the
primary create flow uses) so retries resolve the org scope consistently.

In `@convex/schema.ts`:
- Around line 110-121: Existing brokers lack orgId causing them to be excluded
from the new indexes (by_org, by_org_status) and to break requireOrgIdFromBroker
in convex/lib/orgScope.ts (affecting seedLender/seedMortgage); add a one-off
production backfill that finds brokers with null orgId and populates orgId from
their WorkOS user context (or auditJournal.organizationId similarly) using a
safe migration: write a convex-dev-migrations or admin mutation that queries
records where orgId is null, resolves the correct WorkOS org id per broker (via
the same lookup logic used in requireOrgIdFromBroker), updates each
broker.auditJournal.organizationId and broker.orgId, and run/verify the
migration, or alternatively document and assert that no persisted brokers exist
so the backfill isn’t required.

---

Outside diff comments:
In `@convex/payments/transfers/mutations.ts`:
- Around line 539-552: The current retry-safe fast path in
initiateTransferInternal returns success:true for any transfer.status !==
"initiated", which incorrectly treats "failed" or "cancelled" as successful;
change the condition to only short-circuit for explicitly acceptable progressed
states (e.g., ["pending","confirmed"]) and return the existing
success:true/previousState/newState only for those states; for "failed" or
"cancelled" return a failure result (success:false with a clear reason) or throw
so callers (like createDealClosingPipeline/createAndInitiateLeg2) don't treat
failed/cancelled transfers as successful no-ops.

---

Nitpick comments:
In `@convex/seed/seedDeal.ts`:
- Around line 219-222: Remove the redundant database lookup for mortgageId in
seedDeal.ts: since resolveMortgageIds already validates existence, eliminate the
ctx.db.get(mortgageId) and the subsequent mortgageRow null check/ConvexError
throw; instead use the validated mortgage IDs (or pass the pre-fetched mortgage
documents from resolveMortgageIds into createDeal if available) so seedDeal no
longer performs an extra ctx.db.get for mortgageId.

In `@convex/seed/seedObligation.ts`:
- Around line 229-234: Remove the redundant DB read that assigns mortgageRow by
reusing the already-fetched mortgage (fetched earlier for pair.mortgageId);
delete the await ctx.db.get(...) that creates mortgageRow, replace any uses of
mortgageRow.orgId with mortgage.orgId (including the occurrences noted around
where lines ~258 and ~275 reference orgId), and ensure any null-checks rely on
the existing mortgage variable instead of mortgageRow to avoid duplicate reads.
🪄 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: a717cc2e-2462-4f7d-96b2-bcee1b9c5a6a

📥 Commits

Reviewing files that changed from the base of the PR and between 26fd9cf and 07acb40.

⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (28)
  • convex/demo/simulation.ts
  • convex/dispersal/createDispersalEntries.ts
  • convex/dispersal/disbursementBridge.ts
  • convex/engine/effects/collectionAttempt.ts
  • convex/engine/effects/transfer.ts
  • convex/engine/transition.ts
  • convex/engine/types.ts
  • convex/lib/orgScope.ts
  • convex/obligations/mutations.ts
  • convex/onboarding/mutations.ts
  • convex/payments/obligations/createCorrectiveObligation.ts
  • convex/payments/obligations/generateImpl.ts
  • convex/payments/transfers/depositCollection.ts
  • convex/payments/transfers/mutations.ts
  • convex/payments/transfers/principalReturn.ts
  • convex/schema.ts
  • convex/seed/seedBorrower.ts
  • convex/seed/seedBroker.ts
  • convex/seed/seedDeal.ts
  • convex/seed/seedHelpers.ts
  • convex/seed/seedLender.ts
  • convex/seed/seedMortgage.ts
  • convex/seed/seedObligation.ts
  • convex/seed/seedOnboardingRequest.ts
  • package.json
  • specs/ENG-20/chunks/chunk-01-foundation-and-profiles/context.md
  • specs/ENG-20/chunks/chunk-01-foundation-and-profiles/status.md
  • tsconfig.json

Comment thread convex/lib/orgScope.ts Outdated
Comment thread convex/payments/transfers/mutations.ts Outdated
Comment thread convex/schema.ts
…tions, tests

- Backfill orgId on reused seed mortgages and brokers; broker/lender fallbacks in orgScope
- Corrective obligations + audit journal use derived orgId from mortgage chain
- Bridge transfers and disbursement bridge prefer entry/org resolution paths
- retryTransfer recomputes orgId via orgIdForTransferRequest
- Add convex-dev-migrations backfills for brokers, lenders, audit journal
- Add orgIdForTransferRequest convex-test coverage

Made-with: Cursor
@Connorbelez Connorbelez merged commit 007cb97 into main Mar 29, 2026
1 of 4 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