Skip to content

feat: add origination owner config#434

Open
Connorbelez wants to merge 5 commits intographite-base/434from
default-origination-owner
Open

feat: add origination owner config#434
Connorbelez wants to merge 5 commits intographite-base/434from
default-origination-owner

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Apr 20, 2026

feat: add origination owner config

feat: seed canonical mic owner

feat: issue shares to mic owner

Harden default origination owner seeding and validation

  • Enforce canonical FairLend MIC lender/vehicle contract
  • Reuse existing trust accounts when seeding platform ownership
  • Add commit and seed test coverage for default share issuance

Validate canonical MIC lender identity in seeding

  • Factor the canonical MIC lender email into a shared constant
  • Reject default origination owner configs that point at lookalike lenders
  • Add replay coverage so seeding stays pinned to the original trust account

Summary by Sourcery

Introduce canonical default origination owner configuration tied to the FairLend MIC, and ensure newly originated mortgages automatically issue full-share ownership to this configured platform owner while enforcing strict validation and seeding behavior.

New Features:

  • Add platform-level default origination owner configuration with audited admin mutations and query resolver.
  • Introduce investment vehicle, workspace, and platform settings tables to model MIC ownership and link them into the engine entity model.
  • Seed a canonical FairLend MIC lender, investment vehicle, workspace, trust account, and platform settings via a dedicated seedPlatformOwnership mutation.
  • Automatically issue full mortgage share supply to the configured default origination owner during mortgage activation.

Bug Fixes:

  • Harden origination to fail closed when default origination owner settings are missing or misconfigured, including rejection of lookalike non-FairLend lenders.

Enhancements:

  • Extend seedAll to provision platform ownership artifacts idempotently and include them in seed summaries.
  • Tighten origination commit tests and E2E flows to assert share issuance and ledger/audit artifacts for the configured MIC owner.
  • Strengthen platform governance by writing audit journal entries when the default origination owner configuration changes and by reusing existing trust accounts where possible.

Documentation:

  • Add a detailed implementation plan documenting the default origination owner architecture, schema changes, seeding, and verification strategy.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cafbd38c-d938-4738-bc28-74324212331b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch default-origination-owner

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 20, 2026 21:11
Copilot AI review requested due to automatic review settings April 20, 2026 21:12
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

@Connorbelez Connorbelez mentioned this pull request Apr 20, 2026
Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 20, 2026

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 20, 2026

Reviewer's Guide

Adds a canonical default origination owner configuration for FairLend MIC, seeds and validates it via new platform ownership tables and seed flows, and wires mortgage activation, tests, and e2e flows to mint mortgages and issue full share supply to that configured owner while failing closed on misconfiguration.

Sequence diagram for mortgage activation and share issuance to default origination owner

sequenceDiagram
  actor AdminUser
  participant AdminUI
  participant ConvexServer
  participant ActivateMortgage as activateMortgageAggregate
  participant MintHandler as mintMortgageHandler
  participant OwnerResolver as getRequiredDefaultOriginationOwner
  participant IssueShares as issueSharesHandler
  participant LedgerAccounts
  participant LedgerEntries

  AdminUser->>AdminUI: Commit origination case
  AdminUI->>ConvexServer: admin.origination.commit.commitCase
  ConvexServer->>ActivateMortgage: activateMortgageAggregate(args)

  ActivateMortgage->>MintHandler: mintMortgageHandler(ctx, mortgageMintArgs)
  MintHandler-->>LedgerAccounts: create TREASURY and POSITION accounts
  MintHandler-->>LedgerEntries: insert MORTGAGE_MINTED entry
  MintHandler-->>ActivateMortgage: mintedMortgageId

  ActivateMortgage->>OwnerResolver: getRequiredDefaultOriginationOwner(ctx)
  OwnerResolver->>PlatformSettings: query by_key(key=default)
  OwnerResolver->>Lenders: load defaultOriginationLenderId
  OwnerResolver->>InvestmentVehicles: load defaultOriginationInvestmentVehicleId
  OwnerResolver->>InvestmentVehicleWorkspaces: load defaultOriginationWorkspaceId
  OwnerResolver->>BankAccounts: load defaultFairlendTrustBankAccountId
  OwnerResolver-->>ActivateMortgage: defaultOriginationOwner(lender, investmentVehicle, workspace, trustBankAccount, settings)

  ActivateMortgage->>IssueShares: issueSharesHandler(ctx, issueArgs)
  IssueShares-->>LedgerAccounts: ensure POSITION account for lenderId
  IssueShares-->>LedgerEntries: insert SHARES_ISSUED entry
  IssueShares-->>ActivateMortgage: issueResult

  ActivateMortgage-->>ConvexServer: activationResult with committedMortgageId
  ConvexServer-->>AdminUI: success
  AdminUI-->>AdminUser: Case committed with treasury and MIC position
Loading

Sequence diagram for seeding canonical FairLend MIC platform ownership

sequenceDiagram
  actor Dev
  participant SeedAll as seedAll_adminAction
  participant SeedPlatform as seedPlatformOwnership
  participant SeedHelpers
  participant DefaultOwner as upsertDefaultOriginationOwner
  participant DbPlatform as platformSettings
  participant DbLenders as lenders
  participant DbVehicles as investmentVehicles
  participant DbWorkspaces as investmentVehicleWorkspaces
  participant DbBankAccounts as bankAccounts

  Dev->>SeedAll: run seedAll
  SeedAll->>SeedPlatform: runMutation(seedPlatformOwnership, { brokerId })

  Note over SeedPlatform: ensure canonical FairLend MIC user
  SeedPlatform->>SeedHelpers: ensureUserByEmail(FAIRLEND_MIC_LENDER_EMAIL)
  SeedHelpers-->>SeedPlatform: { userId }

  Note over SeedPlatform: upsert MIC lender
  SeedPlatform->>SeedHelpers: findLenderByUserId(userId)
  SeedHelpers-->>SeedPlatform: existingLender or null
  alt lender exists
    SeedPlatform-->>DbLenders: reuse lenderId
  else lender missing
    SeedPlatform->>DbLenders: insert FairLend MIC lender
    SeedPlatform->>SeedHelpers: writeCreationJournalEntry(lender)
  end

  Note over SeedPlatform: upsert MIC investment vehicle
  SeedPlatform->>DbVehicles: query by_lender(lenderId)
  alt vehicle exists
    SeedPlatform-->>DbVehicles: reuse investmentVehicleId
  else vehicle missing
    SeedPlatform->>DbVehicles: insert MIC vehicle
    SeedPlatform->>SeedHelpers: writeCreationJournalEntry(vehicle)
  end

  Note over SeedPlatform: upsert MIC workspace
  SeedPlatform->>DbWorkspaces: query by_vehicle(investmentVehicleId)
  alt workspace exists
    SeedPlatform-->>DbWorkspaces: reuse workspaceId
  else workspace missing
    SeedPlatform->>DbWorkspaces: insert workspace
    SeedPlatform->>SeedHelpers: writeCreationJournalEntry(workspace)
  end

  Note over SeedPlatform: upsert trust bank account
  SeedPlatform->>DbPlatform: query platformSettings by_key(default)
  DbPlatform-->>SeedPlatform: existingSettings or null
  SeedPlatform->>DbBankAccounts: query by_owner(trust, investmentVehicleId)
  alt preferred or reusable account exists
    SeedPlatform-->>DbBankAccounts: reuse trustBankAccountId
  else none available
    SeedPlatform->>DbBankAccounts: insert trust bank account
  end

  Note over SeedPlatform,DefaultOwner: upsert platformSettings.default
  SeedPlatform->>DefaultOwner: upsertDefaultOriginationOwner(ctx, linkArgs)
  DefaultOwner->>DbPlatform: query by_key(default)
  DefaultOwner->>DbLenders: validate FairLend MIC lender and email
  DefaultOwner->>DbVehicles: validate MIC vehicle name and legalName
  DefaultOwner->>DbWorkspaces: validate workspace link
  DefaultOwner->>DbBankAccounts: validate trust account ownership
  DefaultOwner->>DbPlatform: insert or patch platformSettings.default
  DefaultOwner-->>SeedPlatform: { settings }

  SeedPlatform-->>SeedAll: SeedPlatformOwnershipResult(created, reused, ids)
  SeedAll-->>Dev: aggregated seed summary including platform ownership
Loading

ER diagram for new platform ownership tables and relationships

erDiagram
  Lenders {
    string _id
    string orgId
    string userId
    string brokerId
    string status
    string accreditationStatus
    number createdAt
  }

  InvestmentVehicles {
    string _id
    string lenderId
    string name
    string legalName
    string entityType
    string status
    number createdAt
    number updatedAt
  }

  InvestmentVehicleWorkspaces {
    string _id
    string investmentVehicleId
    string name
    string status
    number createdAt
    number updatedAt
  }

  BankAccounts {
    string _id
    string ownerType
    string ownerId
    string status
    string accountLast4
    string currency
    string country
    number createdAt
    number updatedAt
  }

  PlatformSettings {
    string _id
    string key
    string defaultOriginationLenderId
    string defaultOriginationInvestmentVehicleId
    string defaultOriginationWorkspaceId
    string defaultFairlendTrustBankAccountId
    string updatedBy
    string changeReason
    string source
    number createdAt
    number updatedAt
  }

  Brokers {
    string _id
    string orgId
    string userId
    string status
  }

  Users {
    string _id
    string email
    string authId
  }

  %% Relationships introduced or enforced in this PR
  Lenders ||--o{ InvestmentVehicles : has
  InvestmentVehicles ||--o{ InvestmentVehicleWorkspaces : has

  InvestmentVehicles ||--o{ BankAccounts : owns_via_trust

  PlatformSettings }o--|| Lenders : default_origination_lender
  PlatformSettings }o--|| InvestmentVehicles : default_origination_vehicle
  PlatformSettings }o--o{ InvestmentVehicleWorkspaces : default_workspace
  PlatformSettings }o--o{ BankAccounts : default_trust_account

  Brokers ||--o{ Lenders : has
  Users ||--o{ Lenders : owns
  Brokers ||--o{ Users : uses
Loading

Class diagram for default origination owner and platform ownership seeding modules

classDiagram
  class DefaultOriginationOwnerContract {
    <<module>>
    +string FAIRLEND_MIC_LENDER_EMAIL
    +string FAIRLEND_MIC_INVESTMENT_VEHICLE_NAME
    +string FAIRLEND_MIC_INVESTMENT_VEHICLE_LEGAL_NAME
  }

  class DefaultOriginationOwnerModule {
    <<module>>
    +string PLATFORM_SETTINGS_KEY
    +getRequiredDefaultOriginationOwner(ctx)
    +upsertDefaultOriginationOwner(ctx, args)
    +getDefaultOriginationOwner
    +setDefaultOriginationOwner
    -snapshotPlatformSettings(settings)
    -missingDefaultOriginationOwnerError()
    -invalidDefaultOriginationOwnerError(message)
    -normalizeConfiguredEmail(email)
    -resolveDefaultOriginationOwnerLinks(ctx, linkArgs)
  }

  class DefaultOriginationOwnerCtx {
    +any db
  }

  class DefaultOriginationOwnerLinkArgs {
    +Id~bankAccounts~ defaultFairlendTrustBankAccountId
    +Id~investmentVehicles~ defaultOriginationInvestmentVehicleId
    +Id~lenders~ defaultOriginationLenderId
    +Id~investmentVehicleWorkspaces~ defaultOriginationWorkspaceId
  }

  class UpsertDefaultOriginationOwnerArgs {
    +string actorId
    +ActorType actorType
    +string changeReason
    +CommandChannel channel
    +string source
    +Id~bankAccounts~ defaultFairlendTrustBankAccountId
    +Id~investmentVehicles~ defaultOriginationInvestmentVehicleId
    +Id~lenders~ defaultOriginationLenderId
    +Id~investmentVehicleWorkspaces~ defaultOriginationWorkspaceId
  }

  class SeedPlatformOwnershipModule {
    <<module>>
    +seedPlatformOwnership
    -upsertFairLendMicLender(ctx, args)
    -upsertFairLendMicVehicle(ctx, args)
    -upsertFairLendMicWorkspace(ctx, args)
    -upsertFairLendMicTrustAccount(ctx, args)
  }

  class SeedPlatformOwnershipResult {
    +SeedPlatformOwnershipCreatedCounts created
    +SeedPlatformOwnershipReusedCounts reused
    +Id~bankAccounts~ defaultFairlendTrustBankAccountId
    +Id~investmentVehicles~ defaultOriginationInvestmentVehicleId
    +Id~lenders~ defaultOriginationLenderId
    +Id~investmentVehicleWorkspaces~ defaultOriginationWorkspaceId
    +Id~platformSettings~ settingsId
  }

  class SeedPlatformOwnershipCreatedCounts {
    +number bankAccounts
    +number investmentVehicles
    +number investmentVehicleWorkspaces
    +number lenders
    +number platformSettings
  }

  class SeedPlatformOwnershipReusedCounts {
    +number bankAccounts
    +number investmentVehicles
    +number investmentVehicleWorkspaces
    +number lenders
    +number platformSettings
  }

  class SeedHelpersModule {
    <<module>>
    +ensureUserByEmail(ctx, args)
    +findLenderByUserId(ctx, userId)
    +seedAuthIdFromEmail(email)
    +seedTimestamp(offsetMs)
    +writeCreationJournalEntry(ctx, args)
    +string SEED_SOURCE
  }

  class MortgageActivationModule {
    <<module>>
    +activateMortgageAggregate(ctx, args)
  }

  class IssueSharesHandlerModule {
    <<module>>
    +issueSharesHandler(ctx, args)
  }

  class MintMortgageHandlerModule {
    <<module>>
    +mintMortgageHandler(ctx, args)
  }

  DefaultOriginationOwnerModule --> DefaultOriginationOwnerContract : uses_constants
  SeedPlatformOwnershipModule --> DefaultOriginationOwnerModule : calls_upsertDefaultOriginationOwner
  SeedPlatformOwnershipModule --> DefaultOriginationOwnerContract : uses_constants
  SeedPlatformOwnershipModule --> SeedHelpersModule : uses_seed_helpers
  MortgageActivationModule --> DefaultOriginationOwnerModule : calls_getRequiredDefaultOriginationOwner
  MortgageActivationModule --> IssueSharesHandlerModule : calls_issueSharesHandler
  MortgageActivationModule --> MintMortgageHandlerModule : calls_mintMortgageHandler

  DefaultOriginationOwnerModule ..> DefaultOriginationOwnerCtx : uses
  DefaultOriginationOwnerModule ..> DefaultOriginationOwnerLinkArgs : uses
  DefaultOriginationOwnerModule ..> UpsertDefaultOriginationOwnerArgs : uses

  SeedPlatformOwnershipModule ..> SeedPlatformOwnershipResult : returns
  SeedPlatformOwnershipResult o-- SeedPlatformOwnershipCreatedCounts : has
  SeedPlatformOwnershipResult o-- SeedPlatformOwnershipReusedCounts : has
Loading

File-Level Changes

Change Details Files
Introduce platform ownership data model and engine integration for default origination owner
  • Add new Convex tables for investment vehicles, vehicle workspaces, lender/broker/workspace settings, and a platformSettings singleton keyed by 'default'
  • Extend engine entity typing and validators to cover investmentVehicle, investmentVehicleWorkspace, and platformSetting for audit/journal flows
  • Register new Convex modules and generated API entries for platform default origination owner and platform ownership seeding
convex/schema.ts
convex/engine/types.ts
convex/engine/validators.ts
convex/test/moduleMaps.ts
convex/_generated/api.d.ts
Implement canonical default origination owner contract, resolver, and governance mutation
  • Define canonical FairLend MIC lender email and vehicle naming contract in a shared constants module
  • Add defaultOriginationOwner module that resolves required owner links, enforces FairLend brokerage org and canonical MIC identity, and throws structured ConvexErrors when missing or invalid
  • Implement upsertDefaultOriginationOwner with audit journal writes and a public admin mutation/query for reading and setting platformSettings.default
  • Add unit tests that cover missing settings, valid configuration, non-FairLend lenders, and lookalike MIC impostors
convex/platform/defaultOriginationOwnerContract.ts
convex/platform/defaultOriginationOwner.ts
src/test/convex/platform/defaultOriginationOwner.test.ts
Seed canonical FairLend MIC owner and wire into global seeding
  • Create seedPlatformOwnership mutation that idempotently ensures the FairLend MIC lender, investment vehicle, workspace, and trust bank account exist and are wired into platformSettings.default via upsertDefaultOriginationOwner
  • Reuse existing trust accounts when possible and respect any previously configured default trust account
  • Call seedPlatformOwnership from seedAll, aggregate its created/reused counts, and expose richer SeedPlatformOwnershipResult typing
  • Remove the old Maple MIC lender fixture and update any references to use the canonical fairlend.mic+lender identity instead
  • Add tests to prove idempotent seeding behavior and that seedAll seeds a single platformSettings row, vehicle, and workspace
convex/seed/seedPlatformOwnership.ts
convex/seed/seedAll.ts
convex/seed/seedLender.ts
convex/seed/seedDeal.ts
src/test/convex/seed/seedAll.test.ts
src/test/convex/seed/seedPlatformOwnership.test.ts
src/test/convex/seed/seedPlatformOwnershipChaos.test.ts
Wire default origination owner into mortgage activation and ledger share issuance
  • During activateMortgageAggregate, cache the effectiveDate, call mintMortgageHandler as before, then resolve the required default origination owner and call issueSharesHandler to issue TOTAL_SUPPLY to the configured lender and investment vehicle with appropriate metadata
  • Ensure the new share issuance produces SHARES_ISSUED ledger entries and a POSITION account for the default origination lender while keeping existing audit journal behavior intact
convex/mortgages/activateMortgageAggregate.ts
convex/engine/types.ts
convex/engine/validators.ts
Harden admin origination commit tests around default owner configuration and share issuance
  • Introduce a helper to seed the default origination owner in the commit test harness, optionally skipping it per test
  • Add getConvexErrorData helper to normalize ConvexError payloads for assertions
  • Update happy-path commit tests to seed default owner, then assert both MORTGAGE_MINTED and SHARES_ISSUED ledger entries and that a POSITION account exists for the configured default lender
  • Add failure-path tests verifying commit fails closed when no default owner is configured or when platformSettings points at a non-FairLend owner with detailed error codes/messages
src/test/convex/admin/origination/commit.test.ts
Extend origination E2E harness to ensure and inspect default origination owner behavior
  • Add ensureDefaultOriginationOwner helper to the origination E2E client that seeds a broker and platform ownership, then fetches the resolved default owner via the new platform query
  • Expose a getCommittedOriginationArtifacts test query to return case, listing, mortgage, ledger accounts, and ledger entries for a committed case
  • Update the admin origination Playwright spec to ensure the default owner before running, then verify Convex artifacts include MORTGAGE_MINTED and SHARES_ISSUED entries, a TREASURY account, and a POSITION account for the default lender
e2e/helpers/origination.ts
convex/test/originationE2e.ts
e2e/origination/commit-and-inspect.spec.ts
Add documentation and implementation plan for default origination owner feature
  • Add a detailed implementation plan under docs/superpowers/plans describing the architecture, tasks, and verification steps for default origination owner seeding and usage
docs/superpowers/plans/2026-04-20-default-origination-owner.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

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 a canonical “default origination owner” configuration (pinned to the FairLend MIC) and wires origination to automatically issue 100% of new mortgage share supply to that configured platform owner, with seeding + validation hardening and expanded test coverage.

Changes:

  • Introduces platform ownership settings + resolver/validator (platformSettings["default"]) and an admin mutation/query to manage it with audit journaling.
  • Adds an idempotent-ish seeding path for the canonical FairLend MIC lender/vehicle/workspace/trust bank account and integrates it into seedAll.
  • Updates mortgage activation/origination flows to issue TOTAL_SUPPLY shares to the configured default origination owner, plus new unit/E2E coverage.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/test/convex/seed/seedPlatformOwnershipChaos.test.ts Chaos regression for replay pinning to the original MIC trust account.
src/test/convex/seed/seedPlatformOwnership.test.ts Validates seed idempotency/reuse behavior for canonical ownership artifacts.
src/test/convex/seed/seedAll.test.ts Extends seed-all assertions to include new platform ownership tables/settings.
src/test/convex/platform/defaultOriginationOwner.test.ts Covers fail-closed + governance audit + lookalike lender rejection.
src/test/convex/admin/origination/commit.test.ts Adds seeding + assertions for SHARES_ISSUED and failure-closed paths.
e2e/origination/commit-and-inspect.spec.ts E2E assertions verifying issuance artifacts post-commit.
e2e/helpers/origination.ts Client helpers for ensuring default owner and fetching committed artifacts.
docs/superpowers/plans/2026-04-20-default-origination-owner.md Implementation plan documenting schema/seeding/origination wiring.
convex/test/originationE2e.ts Adds admin query to fetch committed origination artifacts for E2E.
convex/test/moduleMaps.ts Registers new Convex modules for the test harness.
convex/seed/seedPlatformOwnership.ts Seeds canonical MIC ownership artifacts + upserts platform settings.
convex/seed/seedLender.ts Removes the prior “Maple MIC” lender fixture.
convex/seed/seedDeal.ts Updates default lender auth IDs to reference the canonical FairLend MIC.
convex/seed/seedAll.ts Wires platform ownership seeding and extends seed summaries/artifacts.
convex/schema.ts Adds platformSettings, investmentVehicles, investmentVehicleWorkspaces, and settings scaffolds.
convex/platform/defaultOriginationOwnerContract.ts Centralizes canonical MIC identifiers (email/name/legal name).
convex/platform/defaultOriginationOwner.ts Resolver + strict validation + audited upsert for default origination owner.
convex/mortgages/activateMortgageAggregate.ts Issues TOTAL_SUPPLY shares to default owner during activation.
convex/engine/validators.ts Extends audit entity type validator for new entities.
convex/engine/types.ts Extends EntityType + ENTITY_TABLE_MAP for new entities.
convex/_generated/api.d.ts Regenerated API typings for the new modules.

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

"Configured trust account is not owned by the configured investment vehicle"
);
}

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

resolveDefaultOriginationOwnerLinks validates the trust bank account is owned by the configured vehicle, but it doesn’t validate the bank account is actually usable (e.g. status === "validated", and mandate constraints if applicable). As-is, an admin (or migration) could point defaultFairlendTrustBankAccountId at a revoked/rejected/pending_validation account and the config would still pass, likely failing later payment flows. Consider enforcing at least status === "validated" here (and align with the existing transfer validation rules).

Suggested change
if (trustBankAccount && trustBankAccount.status !== "validated") {
throw invalidDefaultOriginationOwnerError(
"Configured trust account is not validated"
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +226 to +285
const now = Date.now();
const existing = await ctx.db
.query("platformSettings")
.withIndex("by_key", (q) => q.eq("key", PLATFORM_SETTINGS_KEY))
.unique();
const beforeState = existing ? snapshotPlatformSettings(existing) : undefined;

const resolved = await resolveDefaultOriginationOwnerLinks(ctx, args);
const nextSettingsFields = {
key: PLATFORM_SETTINGS_KEY,
defaultOriginationLenderId: args.defaultOriginationLenderId,
defaultOriginationInvestmentVehicleId:
args.defaultOriginationInvestmentVehicleId,
defaultOriginationWorkspaceId: args.defaultOriginationWorkspaceId,
defaultFairlendTrustBankAccountId: args.defaultFairlendTrustBankAccountId,
updatedBy: args.actorId,
changeReason: args.changeReason,
source: args.source,
updatedAt: now,
};

let settingsId: Id<"platformSettings">;
if (existing) {
await ctx.db.patch(existing._id, nextSettingsFields);
settingsId = existing._id;
} else {
settingsId = await ctx.db.insert("platformSettings", {
...nextSettingsFields,
createdAt: now,
});
}

const settings = await ctx.db.get(settingsId);
if (!settings) {
throw new Error("Expected platform settings to exist after upsert");
}
const afterState = snapshotPlatformSettings(settings);

await appendAuditJournalEntry(ctx, {
actorId: args.actorId,
actorType: args.actorType,
channel: args.channel,
entityId: String(settingsId),
entityType: "platformSetting",
afterState,
eventCategory: "configuration",
eventType: "DEFAULT_ORIGINATION_OWNER_SET",
newState: "configured",
organizationId: resolved.lender.orgId,
outcome: "transitioned",
payload: {
after: afterState,
before: beforeState ?? null,
changeReason: args.changeReason,
source: args.source,
},
beforeState,
previousState: existing ? "configured" : "none",
timestamp: now,
});
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

upsertDefaultOriginationOwner always patches the existing platformSettings row (updating updatedAt/updatedBy) and always appends an audit journal entry, even when the configured IDs haven’t changed. This makes seedPlatformOwnership/seedAll replays non-idempotent and will spam the audit journal on every seed run. Consider detecting when the settings are already identical and early-returning without patching or writing an audit entry (similar to how other seed/config upserts skip no-op patches).

Copilot uses AI. Check for mistakes.
- Enforce canonical FairLend MIC lender/vehicle contract
- Reuse existing trust accounts when seeding platform ownership
- Add commit and seed test coverage for default share issuance
- Factor the canonical MIC lender email into a shared constant
- Reject default origination owner configs that point at lookalike lenders
- Add replay coverage so seeding stays pinned to the original trust account
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