Skip to content

eng-308-lender portfolio#470

Open
Connorbelez wants to merge 3 commits intocodex/eng-302-explicit-borrower-portal-attributionfrom
eng308-lender-portfolio
Open

eng-308-lender portfolio#470
Connorbelez wants to merge 3 commits intocodex/eng-302-explicit-borrower-portal-attributionfrom
eng308-lender-portfolio

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

fix(portals): address eng-302 review feedback

Implemented ENG-308 in a dedicated portfolio backend module. contracts.ts, helpers.ts, and queries.ts now define the command-center contract plus the { portalId, mortgageId } and { portalId, obligationId } detail reads, all behind portalLenderQuery and portfolio:view. I also extracted shared lender-constraint logic into lenderConstraints.ts and rewired portalQueries.ts to reuse it, so lender listing limits and portfolio suggestions now share one source of truth. The new portfolio suite in queries.test.ts covers empty state, unauthorized access, ownership math, individual payment rows, broker context, and ordered suggestion exclusion. The ENG-308 workflow artifacts under status.md and audit.md are updated through a passing final validator.

clampEnumValues in lenderConstraints.ts now distinguishes allowed === undefined from allowed === [], so explicit empty allowlists stay deny-all instead of disabling lender constraints. In helpers.ts, the command-center suggestion rail now keeps paging marketplace results until it collects 5 non-owned suggestions or exhausts the filtered set, instead of truncating before owned-listing exclusion.

I also added regression coverage in queries.test.ts and queries.test.ts for empty stored allowlists and for backfilling suggestions when owned listings dominate the first page. Validation passed: bun check completed with the repo’s existing out-of-scope complexity warnings, bunx convex codegen passed, bun typecheck passed, and bun run test -- convex/portfolio/tests/queries.test.ts convex/listings/tests/queries.test.ts passed with 25 tests.

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, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 610

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Important

Review skipped

Draft detected.

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: c80f6390-d8d5-4d8f-975d-56aaec05c7ba

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 eng308-lender-portfolio

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.

This was referenced Apr 21, 2026
Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 21, 2026

…s.ts, helpers.ts, and queries.ts now define the command-center contract plus the { portalId, mortgageId } and { portalId, obligationId } detail reads, all behind portalLenderQuery and portfolio:view. I also extracted shared lender-constraint logic into lenderConstraints.ts and rewired portalQueries.ts to reuse it, so lender listing limits and portfolio suggestions now share one source of truth. The new portfolio suite in queries.test.ts covers empty state, unauthorized access, ownership math, individual payment rows, broker context, and ordered suggestion exclusion. The ENG-308 workflow artifacts under status.md and audit.md are updated through a passing final validator.
… undefined from allowed === [], so explicit empty allowlists stay deny-all instead of disabling lender constraints. In helpers.ts, the command-center suggestion rail now keeps paging marketplace results until it collects 5 non-owned suggestions or exhausts the filtered set, instead of truncating before owned-listing exclusion.

I also added regression coverage in queries.test.ts and queries.test.ts for empty stored allowlists and for backfilling suggestions when owned listings dominate the first page. Validation passed: bun check completed with the repo’s existing out-of-scope complexity warnings, bunx convex codegen passed, bun typecheck passed, and bun run test -- convex/portfolio/__tests__/queries.test.ts convex/listings/__tests__/queries.test.ts passed with 25 tests.
@Connorbelez Connorbelez force-pushed the eng308-lender-portfolio branch from a8482cb to 4c613e9 Compare April 21, 2026 20:29
@Connorbelez Connorbelez changed the title fix(portals): address eng-302 review feedback eng-308-lender portfolio Apr 21, 2026
@Connorbelez Connorbelez marked this pull request as ready for review April 21, 2026 20:30
Copilot AI review requested due to automatic review settings April 21, 2026 20:30
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

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

Implements ENG-308 by introducing a dedicated convex/portfolio backend module that exposes lender portfolio command-center and detail-sheet queries (position/payment) using portalLenderQuery + portfolio:view, and centralizes lender listing constraint logic for reuse across listings and portfolio.

Changes:

  • Added portfolio contracts, helper composition logic, and public queries for command-center + detail sheets.
  • Extracted lender constraint loading/clamping into convex/listings/lenderConstraints.ts and rewired portal listing queries to use it.
  • Added focused portfolio query tests and updated listing tests for the empty-allowlist (deny-all) regression; updated ENG-308 spec artifacts.

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
specs/ENG-308/tasks.md Task breakdown for ENG-308 execution.
specs/ENG-308/summary.md Scope/constraints summary for the ENG-308 backend slice.
specs/ENG-308/status.md Execution status + validation notes.
specs/ENG-308/execution-checklist.md Requirements/DoD checklist and coverage expectations.
specs/ENG-308/chunks/manifest.md Chunk manifest for the ENG-308 implementation plan.
specs/ENG-308/chunks/chunk-01-constraints-and-contracts/* Chunk artifacts documenting constraint extraction + contract scaffolding.
specs/ENG-308/chunks/chunk-02-command-center-query/* Chunk artifacts documenting the command-center query work.
specs/ENG-308/chunks/chunk-03-detail-queries-and-tests/* Chunk artifacts documenting detail queries + tests + validation.
specs/ENG-308/audit.md Spec compliance audit ledger and evidence pointers.
convex/test/moduleMaps.ts Registers new modules in the Convex test module map.
convex/portfolio/queries.ts Public, portal-scoped lender portfolio query endpoints.
convex/portfolio/helpers.ts Portfolio composition logic for cockpit/positions/payments/actions/limits/suggestions and detail sheets.
convex/portfolio/contracts.ts DTO validators + inferred TS types for portfolio payloads.
convex/portfolio/tests/queries.test.ts Portfolio query test suite covering empty state, auth, ownership math, details, suggestions, broker context.
convex/listings/portalQueries.ts Reuses extracted lender constraint helpers for lender portal listing visibility and effective filters.
convex/listings/lenderConstraints.ts New centralized lender constraint loader + filter clamping utilities.
convex/listings/tests/queries.test.ts Regression test for empty allowlists behaving as deny-all.
convex/_generated/api.d.ts Codegen update to include new modules.
CLAUDE.md Updates GitNexus index metadata.
AGENTS.md Updates GitNexus index metadata.

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

Comment on lines +1111 to +1115
const positionUnits =
positions.find(
(position) => String(position.mortgageId) === String(mortgage._id)
)?.balanceUnits ?? 0;
return accumulateBreakdown(rows, mortgage.status, positionUnits);
Comment on lines +253 to +260
const legacyAccounts = (
await ctx.db.query("ledger_accounts").collect()
).filter(
(account) =>
account.type === "POSITION" &&
getAccountLenderId(account) === lenderAuthId
);

Comment on lines +400 to +407
const transferEntries = await Promise.all(
obligations.map(async (obligation) => {
const transfers = await ctx.db
.query("transferRequests")
.withIndex("by_obligation", (query) =>
query.eq("obligationId", obligation._id)
)
.collect();
Comment on lines +475 to +498
const pageCandidates = await Promise.all(
snapshot.page.map(async (suggestion) => {
const listingId = suggestion.id as Id<"listings">;
const listing = await ctx.db.get(listingId);
if (!listing) {
return null;
}
return { listing, suggestion };
})
);

for (const candidate of pageCandidates) {
if (!candidate) {
continue;
}
if (
candidate.listing.mortgageId &&
args.heldMortgageIds.has(String(candidate.listing.mortgageId))
) {
excludedOwnedMortgageCount += 1;
continue;
}
if (rows.length < SUGGESTED_OPPORTUNITY_LIMIT) {
rows.push(candidate);
Comment on lines +465 to +474
do {
const snapshot = await listMarketplaceListingsSnapshot(
ctx,
{
cursor,
filters: args.filters,
numItems: SUGGESTED_OPPORTUNITY_PAGE_SIZE,
},
{ pricingPolicy: args.pricingPolicy }
);
PortfolioTimelineEvent,
} from "./contracts";

const DAYS_PER_MILLISECOND = 1000 * 60 * 60 * 24;
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