Skip to content

eng-219#305

Merged
Connorbelez merged 5 commits intomainfrom
Connorbelez/eng-219-disbursement-snapshots
Mar 29, 2026
Merged

eng-219#305
Connorbelez merged 5 commits intomainfrom
Connorbelez/eng-219-disbursement-snapshots

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Mar 28, 2026

TL;DR

Implemented effective-date ownership snapshot enforcement for dispersal entries to prevent post-calculation reroutes from affecting disbursement amounts.

What changed?

Added audit metadata fields ownershipSnapshotDate and reroutesAppliedCount to dispersal entry calculation details. The applyDealReroutes function now returns the count of applied reroutes, and both fields are recorded during dispersal creation. Added defensive assertions in the disbursement bridge to validate calculation details exist and that entry amounts don't exceed distributable amounts.

How to test?

Run the dispersal test suite to verify:

  • Snapshot metadata is correctly recorded during dispersal creation
  • Reroutes applied after dispersal calculation but before disbursement don't change the disbursement amount
  • Bridge properly rejects entries with missing calculation details or amounts exceeding distributable limits
bun run test -- --run convex/dispersal/__tests__/

Why make this change?

Ensures temporal consistency in the dispersal system by capturing ownership state at the time of calculation and preventing subsequent ownership changes from affecting already-calculated disbursement amounts. This maintains audit trails and prevents potential financial discrepancies.

Summary by CodeRabbit

  • New Features

    • Dispersal entries now record ownership snapshot dates and the count of reroutes applied.
    • Disbursement processing includes new validation gates to ensure calculation details are present and amounts do not exceed distributable values.
  • Tests

    • Added tests validating snapshot metadata persistence and reroute scenarios.
    • Added tests covering disbursement validation failures for missing calculation details and excessive amounts.
  • Documentation

    • New spec and task docs describing schema/backend changes and test plans.

@linear
Copy link
Copy Markdown

linear bot commented Mar 28, 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 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ab1066b-f8b1-40fd-9d10-29dcc5cbf745

📥 Commits

Reviewing files that changed from the base of the PR and between 06d6698 and 8d8dce7.

📒 Files selected for processing (4)
  • convex/dispersal/__tests__/createDispersalEntries.test.ts
  • convex/dispersal/disbursementBridge.ts
  • specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md
  • specs/ENG-219/chunks/chunk-02-tests/tasks.md
✅ Files skipped from review due to trivial changes (3)
  • convex/dispersal/tests/createDispersalEntries.test.ts
  • specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md
  • specs/ENG-219/chunks/chunk-02-tests/tasks.md

📝 Walkthrough

Walkthrough

This PR records ownership-snapshot metadata on dispersal entries and tightens disbursement validation. CalculationDetails gains ownershipSnapshotDate and reroutesAppliedCount; applyDealReroutes returns a count persisted on insert; processSingleDisbursement now asserts presence/validity of calculation details and distributable limits before creating transfer requests.

Changes

Cohort / File(s) Summary
Type System & Validator
convex/dispersal/types.ts, convex/dispersal/validators.ts
Added optional ownershipSnapshotDate?: string and reroutesAppliedCount?: number to the CalculationDetails interface and the Convex validation schema.
Dispersal Entry Creation
convex/dispersal/createDispersalEntries.ts
applyDealReroutes now returns Promise<number> (count of applied reroutes); createDispersalEntries captures that count and persists it plus ownershipSnapshotDate (from args.settledDate) into calculationDetails.
Disbursement Bridge
convex/dispersal/disbursementBridge.ts
processSingleDisbursement adds two pre-transfer assertions: throws MISSING_CALCULATION_DETAILS if calculationDetails or positive settledAmount/valid distributableAmount are missing; throws AMOUNT_EXCEEDS_DISTRIBUTABLE if entry.amount > calculationDetails.distributableAmount.
Tests — createDispersalEntries
convex/dispersal/__tests__/createDispersalEntries.test.ts
Added ENG-219 tests asserting persisted calculationDetails.ownershipSnapshotDate equals supplied settledDate and calculationDetails.reroutesAppliedCount reflects 0 or 1 applied reroutes.
Tests — disbursementBridge
convex/dispersal/__tests__/disbursementBridge.test.ts
Added ENG-219 bridge tests: reroute-timing edge case (transfer amount unchanged), missing calculation-details rejection, amount-exceeds-distributable rejection; adjusted a seeded test amount for precision.
Specs / Documentation
specs/ENG-219/*
Added ENG-219 spec chunks, tasks, and manifest documenting schema/backend changes and tests for snapshot metadata and new bridge assertions.

Sequence Diagram(s)

sequenceDiagram
participant Creator as CreateDispersalEntries
participant Rerouter as applyDealReroutes
participant DB as DispersalEntries (DB)

Creator->>Rerouter: compute & attempt reroutes
Rerouter-->>Creator: number of applied reroutes (n)
Creator->>DB: insert dispersalEntry with calculationDetails { ownershipSnapshotDate, reroutesAppliedCount: n }
DB-->>Creator: persisted entry id
Loading
sequenceDiagram
participant Bridge as processSingleDisbursement
participant DB as DispersalEntries (DB)
participant Ledger as CashLedger / Provider
participant Transfers as TransferRequests (DB)

Bridge->>DB: re-read dispersal entry
DB-->>Bridge: entry (incl. calculationDetails)
Bridge->>Bridge: validate calculationDetails present & positive settledAmount
alt amount <= distributableAmount
Bridge->>Ledger: balance & provider checks...
Ledger-->>Bridge: ok
Bridge->>Transfers: insert transferRequest
Transfers-->>Bridge: transfer recorded
else amount > distributableAmount
Bridge-->>Bridge: throw AMOUNT_EXCEEDS_DISTRIBUTABLE
end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through code to mark the date,

ownership snapshots now record their fate.
Reroutes counted, kept in sight,
Bridges guard the transfer right,
A tiny rabbit cheers the ledger’s state.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "eng-219" is a ticket/issue identifier that refers to the overall engineering feature but does not describe the actual change itself—it lacks specificity about the primary change (ownership snapshot enforcement for dispersal entries). Consider using a more descriptive title that captures the main change, such as "Add ownership snapshot enforcement and audit metadata to dispersal entries" or "Record ownership snapshot date and reroutes applied count during dispersal creation."
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Connorbelez/eng-219-disbursement-snapshots

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 28, 2026 23:01
Copilot AI review requested due to automatic review settings March 28, 2026 23:01
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-219’s “effective-date ownership snapshot” enforcement by persisting snapshot audit metadata on dispersal entries and adding defensive validation in the disbursement bridge so post-calculation ownership changes can’t affect disbursement amounts.

Changes:

  • Added optional audit metadata fields (ownershipSnapshotDate, reroutesAppliedCount) to dispersal calculationDetails (validator + TS types) and persisted them during dispersal creation.
  • Updated applyDealReroutes to return the number of reroutes actually applied and recorded that count in calculationDetails.
  • Added bridge assertions for missing/invalid calculation details and for entry amounts exceeding distributableAmount, plus tests covering the new behavior.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
specs/ENG-219/tasks.md Master task list for ENG-219.
specs/ENG-219/chunks/manifest.md Chunk manifest for ENG-219 work breakdown.
specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md Chunk 01 task checklist (schema/backend).
specs/ENG-219/chunks/chunk-01-schema-backend/context.md Implementation context/plan for schema/backend updates.
specs/ENG-219/chunks/chunk-02-tests/tasks.md Chunk 02 task checklist (tests).
specs/ENG-219/chunks/chunk-02-tests/context.md Test plan/context for ENG-219 scenarios.
convex/dispersal/validators.ts Extends calculationDetailsValidator with optional snapshot audit fields.
convex/dispersal/types.ts Extends CalculationDetails interface with optional snapshot audit fields.
convex/dispersal/createDispersalEntries.ts Returns applied reroute count and persists snapshot metadata to entries.
convex/dispersal/disbursementBridge.ts Adds defensive assertions for missing calc details and distributable amount limit.
convex/dispersal/tests/disbursementBridge.test.ts Adds ENG-219 tests and adjusts a gate test to satisfy new assertions.
convex/dispersal/tests/createDispersalEntries.test.ts Adds metadata persistence tests for snapshot date and reroute count.

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

Comment thread convex/dispersal/disbursementBridge.ts
Comment thread convex/dispersal/__tests__/createDispersalEntries.test.ts Outdated
Comment thread convex/dispersal/__tests__/createDispersalEntries.test.ts Outdated
Comment thread specs/ENG-219/chunks/chunk-02-tests/tasks.md Outdated
Comment thread specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md 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.

🧹 Nitpick comments (3)
specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md (1)

4-8: Minor: Task checkboxes are unchecked but master list shows tasks as complete.

These tasks are listed as - [ ] (unchecked) while specs/ENG-219/tasks.md marks them as [x] (complete). Consider updating for consistency.

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

In `@specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md` around lines 4 - 8,
The checklist in specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md shows
T-001 through T-005 as unchecked; update those five items to checked to match
the master spec (specs/ENG-219/tasks.md) that marks them complete. Specifically
change the list entries for T-001, T-002, T-003, T-004, and T-005 in
chunk-01-schema-backend/tasks.md to use the completed checkbox state so they are
consistent with the master task file.
specs/ENG-219/chunks/chunk-02-tests/tasks.md (1)

4-7: Minor: Task checkboxes are unchecked but manifest shows chunk as complete.

The tasks here are listed as - [ ] (unchecked), while specs/ENG-219/chunks/manifest.md marks chunk 02 as "complete". Consider updating to - [x] for consistency with the manifest status.

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

In `@specs/ENG-219/chunks/chunk-02-tests/tasks.md` around lines 4 - 7, The task
list for chunk 02 shows unchecked items but the manifest marks the chunk as
complete; update the four checklist lines for T-006, T-007, T-008, and T-009 by
changing each "- [ ]" to "- [x]" so the tasks (T-006 through T-009) reflect the
completed status and remain consistent with the manifest.
specs/ENG-219/chunks/chunk-02-tests/context.md (1)

17-19: Clarify reroute narrative to avoid contradictory test guidance.

These sections mix “must match lender auth ID / after dispersal” guidance with later clarification that the bridge only consumes entry.amount. Consider simplifying to “insert reroute after entry creation; identity matching is not required for this bridge passthrough assertion” so the instructions stay internally consistent.

As per coding guidelines, Markdown findings should focus on architecture/implementation consistency, not formatting.

Also applies to: 28-40, 47-54

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

In `@specs/ENG-219/chunks/chunk-02-tests/context.md` around lines 17 - 19, Update
the narrative to remove the contradictory identity-matching requirement and
clearly state that you should insert a dealReroutes record (with
effectiveAfterDate set after dispersal but before disbursement) after the entry
creation; emphasize that processSingleDisbursement (via the bridge) only
consumes entry.amount and therefore the test should assert the transfer uses the
ORIGINAL amount (45_000) — identity/lender auth ID matching is not required for
this passthrough assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md`:
- Around line 4-8: The checklist in
specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md shows T-001 through T-005
as unchecked; update those five items to checked to match the master spec
(specs/ENG-219/tasks.md) that marks them complete. Specifically change the list
entries for T-001, T-002, T-003, T-004, and T-005 in
chunk-01-schema-backend/tasks.md to use the completed checkbox state so they are
consistent with the master task file.

In `@specs/ENG-219/chunks/chunk-02-tests/context.md`:
- Around line 17-19: Update the narrative to remove the contradictory
identity-matching requirement and clearly state that you should insert a
dealReroutes record (with effectiveAfterDate set after dispersal but before
disbursement) after the entry creation; emphasize that processSingleDisbursement
(via the bridge) only consumes entry.amount and therefore the test should assert
the transfer uses the ORIGINAL amount (45_000) — identity/lender auth ID
matching is not required for this passthrough assertion.

In `@specs/ENG-219/chunks/chunk-02-tests/tasks.md`:
- Around line 4-7: The task list for chunk 02 shows unchecked items but the
manifest marks the chunk as complete; update the four checklist lines for T-006,
T-007, T-008, and T-009 by changing each "- [ ]" to "- [x]" so the tasks (T-006
through T-009) reflect the completed status and remain consistent with the
manifest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c565559-cfed-40e9-a3e7-2d65e0dedaca

📥 Commits

Reviewing files that changed from the base of the PR and between 8b92887 and 06d6698.

📒 Files selected for processing (12)
  • convex/dispersal/__tests__/createDispersalEntries.test.ts
  • convex/dispersal/__tests__/disbursementBridge.test.ts
  • convex/dispersal/createDispersalEntries.ts
  • convex/dispersal/disbursementBridge.ts
  • convex/dispersal/types.ts
  • convex/dispersal/validators.ts
  • specs/ENG-219/chunks/chunk-01-schema-backend/context.md
  • specs/ENG-219/chunks/chunk-01-schema-backend/tasks.md
  • specs/ENG-219/chunks/chunk-02-tests/context.md
  • specs/ENG-219/chunks/chunk-02-tests/tasks.md
  • specs/ENG-219/chunks/manifest.md
  • specs/ENG-219/tasks.md

Connorbelez and others added 4 commits March 28, 2026 19:09
…ursementBridge

Extend the calculationDetails guard (step 2b) to also assert that
`distributableAmount` is a finite number via `Number.isFinite` before
the `entry.amount > entry.calculationDetails.distributableAmount`
comparison is reached. Also upgrade the `settledAmount` check from
`typeof ... !== "number"` to `Number.isFinite` for consistency, so
both fields reject NaN, Infinity, and undefined/missing values from
legacy or corrupted rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… regressions)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Connorbelez Connorbelez merged commit 26fd9cf into main Mar 29, 2026
1 of 3 checks passed
Connorbelez added a commit that referenced this pull request Apr 20, 2026
### TL;DR

Implemented effective-date ownership snapshot enforcement for dispersal entries to prevent post-calculation reroutes from affecting disbursement amounts.

### What changed?

Added audit metadata fields `ownershipSnapshotDate` and `reroutesAppliedCount` to dispersal entry calculation details. The `applyDealReroutes` function now returns the count of applied reroutes, and both fields are recorded during dispersal creation. Added defensive assertions in the disbursement bridge to validate calculation details exist and that entry amounts don't exceed distributable amounts.

### How to test?

Run the dispersal test suite to verify:
- Snapshot metadata is correctly recorded during dispersal creation
- Reroutes applied after dispersal calculation but before disbursement don't change the disbursement amount
- Bridge properly rejects entries with missing calculation details or amounts exceeding distributable limits

```bash
bun run test -- --run convex/dispersal/__tests__/
```

### Why make this change?

Ensures temporal consistency in the dispersal system by capturing ownership state at the time of calculation and preventing subsequent ownership changes from affecting already-calculated disbursement amounts. This maintains audit trails and prevents potential financial discrepancies.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Dispersal entries now record ownership snapshot dates and the count of reroutes applied.
  * Disbursement processing includes new validation gates to ensure calculation details are present and amounts do not exceed distributable values.

* **Tests**
  * Added tests validating snapshot metadata persistence and reroute scenarios.
  * Added tests covering disbursement validation failures for missing calculation details and excessive amounts.

* **Documentation**
  * New spec and task docs describing schema/backend changes and test plans.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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