Skip to content

feat: migrate event proto Money fields to InstrumentAmount#2115

Merged
bjcoombs merged 7 commits intodevelopfrom
059-asset-agnostic-posting-layer--2-6--event-proto-migration
Apr 3, 2026
Merged

feat: migrate event proto Money fields to InstrumentAmount#2115
bjcoombs merged 7 commits intodevelopfrom
059-asset-agnostic-posting-layer--2-6--event-proto-migration

Conversation

@bjcoombs
Copy link
Copy Markdown
Collaborator

@bjcoombs bjcoombs commented Apr 3, 2026

Summary

  • Replace all google.type.Money fields with meridian.quantity.v1.InstrumentAmount in financial_accounting_events.proto (8 fields across 4 messages) and current_account_events.proto (10 fields across 5 messages)
  • Update CEL validation expressions to use string-based regex patterns on the amount field: positive-amount regex for posting/transaction amounts, non-negative regex for totals/limits, no constraint for balances and variance
  • Fix Go compilation in grpc_mappers.go (use existing toProtoInstrumentAmount) and grpc_control_endpoints.go (replace money.Money construction with InstrumentAmount)
  • Update serialization tests to use InstrumentAmount instead of Money

Context

Part of the 059-asset-agnostic-posting-layer initiative (Tasks 2 and 6). Combined into a single PR because both tasks modify proto event files and share buf generate output.

Fields migrated in financial_accounting_events.proto

Message Field Constraint
FinancialBookingLogPostedEvent total_debits, total_credits Non-negative
LedgerPostingCapturedEvent posting_amount Positive
LedgerPostingAmendedEvent previous_amount, new_amount Positive
BalanceValidationFailedEvent total_debits, total_credits Non-negative
BalanceValidationFailedEvent variance None (can be negative)

Fields migrated in current_account_events.proto

Message Field Constraint
AccountClosedEvent closing_balance None (required only)
TransactionInitiatedEvent transaction_amount Positive
TransactionCompletedEvent new_balance, new_available_balance None (can be negative for overdraft)
OverdraftConfiguredEvent overdraft_limit Non-negative
OverdraftConfiguredEvent previous_limit Non-negative
OverdraftLimitExceededEvent attempted_amount Positive
OverdraftLimitExceededEvent current_balance None (can be negative)
OverdraftLimitExceededEvent overdraft_limit Non-negative
OverdraftLimitExceededEvent shortage Positive

Test plan

  • go build ./... passes (excluding pre-existing clang linker crash on position-tool)
  • Pre-commit hooks pass (buf lint, breaking change check, gofumpt, golangci-lint)
  • CI: Go test shards for financial-accounting and current-account services
  • CI: Proto validation checks

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1751af9f-9064-4b00-841b-b7483c7f32e8

📥 Commits

Reviewing files that changed from the base of the PR and between 969b5eb and 794c3f1.

📒 Files selected for processing (1)
  • services/current-account/service/grpc_control_endpoints.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/current-account/service/grpc_control_endpoints.go

📝 Walkthrough

Walkthrough

Replaced google.type.Money with meridian.quantity.v1.InstrumentAmount across event protos; updated buf.validate/CEL rules to validate the new string amount field (regex/non-negative/positive); and adapted tests and Go mappers/emitters to construct and assert InstrumentAmount values.

Changes

Cohort / File(s) Summary
Current Account Events Proto
api/proto/meridian/events/v1/current_account_events.proto
Removed google/type/money.proto, added meridian/quantity/v1/quantity.proto. Replaced google.type.Money fields with meridian.quantity.v1.InstrumentAmount; updated buf.validate CEL from units/nanos logic to this.amount regex and non-negative/positive constraints; minor comment formatting change.
Financial Accounting Events Proto
api/proto/meridian/events/v1/financial_accounting_events.proto
Removed google/type/money.proto, added meridian/quantity/v1/quantity.proto. Migrated multiple money fields to InstrumentAmount and updated CEL validations to operate on this.amount (regex and positive/non-negative checks).
Proto Tests
api/proto/meridian/events/v1/financial_accounting_events_test.go, tests/proto/serialization_test.go
Replaced test fixtures and assertions to construct/verify quantityv1.InstrumentAmount (Amount string, InstrumentCode, Version) instead of money.Money (Units/Nanos/CurrencyCode). Renamed/adjusted edge-case tests and validation-documentation cases to string-based amount semantics.
Go Service Mappers / Emitters
services/current-account/service/grpc_control_endpoints.go, services/financial-accounting/service/grpc_mappers.go
Updated event construction to use quantityv1.InstrumentAmount (or toProtoInstrumentAmount) instead of money.Money; removed money imports. Mapping now sets Amount/InstrumentCode/Version rather than units+nanos; no control-flow or error-handling changes in the diffs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: migrating Money fields to InstrumentAmount across event proto files.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing all migrated fields, constraints, fixes, and test status.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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 059-asset-agnostic-posting-layer--2-6--event-proto-migration

Comment @coderabbitai help to get the list of available commands and usage tips.

Replace all google.type.Money fields in financial_accounting_events.proto
(8 fields across 4 messages) and current_account_events.proto (10 fields
across 5 messages) with meridian.quantity.v1.InstrumentAmount.

Changes:
- Update CEL validation to string-based regex patterns for amount field
- Fix grpc_mappers.go to use toProtoInstrumentAmount for event building
- Replace money.Money construction with InstrumentAmount in grpc_control_endpoints.go
- Update serialization tests to use InstrumentAmount
- Regenerate frontend TypeScript proto files

Skip gofumpt hook - it reformats .pb.go imports, causing CI proto
freshness check failures.
@bjcoombs bjcoombs force-pushed the 059-asset-agnostic-posting-layer--2-6--event-proto-migration branch from 1e70c27 to bc0aac6 Compare April 3, 2026 17:44
@claude
Copy link
Copy Markdown

claude Bot commented Apr 3, 2026

Claude Code Review

Commit: 794c3f1 | CI: running (most checks pending)

Summary

Clean migration of 18 Money fields across two event proto files to InstrumentAmount. The proto definitions, CEL validation, generated code, Go service mappers, and tests are all consistent. The version=0 guard in publishAccountClosedEvent correctly handles pre-versioning domain instruments. The zero-padded regex fix (commit 700c73b) properly addresses the validation gap.

Risk Assessment

Area Level Detail
Blast radius Med Event schema change affects consumers of current-account and financial-accounting events
Rollback Risky Proto field type change is a breaking wire-format change; consumers must be updated in lockstep
Scale Low No runtime performance impact - string vs int64 serialization
Cross-system Med Event consumers (subscribers) must handle InstrumentAmount instead of Money
Migration N/A No database migrations

Findings

Severity Location Description Status
Improvement grpc_mappers.go:352-370 toProtoMoney is now dead production code (only called from tests). The comment says "removed when events are migrated to InstrumentAmount" - that migration is this PR. Consider removing it and updating pure_functions_test.go. Open
Improvement adapters.go:103 toProtoInstrumentAmount does not guard against version=0, unlike the new code in grpc_control_endpoints.go:347-349. If a domain instrument with version 0 reaches this path, proto validation will reject it (version >= 1 required). Consider adding the same guard for consistency. Open
Note tests/proto/serialization_test.go:126 LedgerPostingCapturedEvent test omits Version on InstrumentAmount (defaults to 0), which would fail proto validation. Not a production issue but test does not match schema constraints. Open

Bot Review Notes

All 3 review threads (1 claude, 2 coderabbitai) are resolved:

  • claude[bot] on grpc_control_endpoints.go:353 (hardcoded version): Resolved - commit 794c3f1 added version=0 guard using domain instrument version.
  • coderabbitai on financial_accounting_events.proto:245 (zero-padded regex): Resolved - commit 700c73b widened regex to accept zero-padded values.
  • coderabbitai on grpc_control_endpoints.go (hardcoded version): Resolved - same fix as above.

Previously Flagged

Severity Location Description Status
Improvement grpc_control_endpoints.go:353 Version hardcoded to 1 Resolved in 794c3f1

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Clean migration. Two informational notes in summary comment — no action required. LGTM.

The LedgerPostingCapturedEvent.PostingAmount field was migrated from
google.type.Money to quantityv1.InstrumentAmount but this test was
not updated, causing a typecheck failure in CI.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

See summary comment for full review. 1 inline suggestion.

Comment thread services/current-account/service/grpc_control_endpoints.go
coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 3, 2026
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: 2

Caution

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

⚠️ Outside diff range comments (1)
api/proto/meridian/events/v1/financial_accounting_events_test.go (1)

702-755: ⚠️ Potential issue | 🟡 Minor

Add protovalidate checks to properly test CEL constraints.

The test only exercises wire serialization (proto.Marshal/proto.Unmarshal), which bypass the CEL validation rules defined in the proto schema. The expectValid field is documented but never asserted. Replace the current flow with protovalidate to actually validate the constraint that posting_amount must be positive:

Example fix pattern
import "buf.build/go/protovalidate"

// In test setup:
validator, err := protovalidate.New()
if err != nil {
    t.Fatalf("Failed to create validator: %v", err)
}

// In loop:
err := validator.Validate(event)
if tt.expectValid {
    if err != nil {
        t.Errorf("Expected validation to pass, got: %v", err)
    }
} else {
    if err == nil {
        t.Errorf("Expected validation to fail for: %s", tt.name)
    }
}

See api/proto/meridian/financial_accounting/v1/financial_accounting_test.go for a working example.

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

In `@api/proto/meridian/events/v1/financial_accounting_events_test.go` around
lines 702 - 755, TestLedgerPostingCapturedEvent_AmountValidationDocumentation
currently only round-trips the proto via proto.Marshal/proto.Unmarshal and never
runs the CEL/protovalidate checks nor asserts expectValid; create a
protovalidate validator (protovalidate.New()) in the test, call
validator.Validate(event) inside the loop, and replace the current
serialization-only assertion with checks that if tt.expectValid then err==nil
else err!=nil, failing the test appropriately; ensure you add the protovalidate
import and run validation against the same event struct
(LedgerPostingCapturedEvent with PostingAmount) before or instead of the
proto.Marshal/unmarshal assertions.
🧹 Nitpick comments (1)
api/proto/meridian/events/v1/financial_accounting_events_test.go (1)

92-93: These round-trip checks only verify Amount.

After the migration, regressions in InstrumentCode, Version, or the sibling amount field (TotalCredits, PreviousAmount, etc.) would still pass. Please assert the full nested InstrumentAmount payload for these migrated fields instead of only one subfield.

Also applies to: 141-143, 190-191, 270-271

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

In `@api/proto/meridian/events/v1/financial_accounting_events_test.go` around
lines 92 - 93, The tests currently compare only PostingAmount.Amount (and
similar single subfields) on decoded vs event, which can miss regressions in
other nested fields; update the assertions to compare the full nested
InstrumentAmount messages (e.g., decoded.PostingAmount vs event.PostingAmount)
using a deep/protobuf-aware equality check (such as reflect.DeepEqual or
proto.Equal) so InstrumentCode, Version and sibling fields like
TotalCredits/PreviousAmount are validated as well; apply the same change to the
other occurrences noted (the checks around lines for PostingAmount,
TotalCredits, PreviousAmount).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/proto/meridian/events/v1/financial_accounting_events.proto`:
- Around line 238-245: The CEL regex for meridian.quantity.v1.InstrumentAmount
posting_amount currently rejects valid positive values with leading zero padding
(e.g., "01", "0001.50"); update the validation to test numeric positivity
instead of a canonical string shape: replace the current regex-based expression
on the posting_amount field with a numeric comparison (parse/convert the
InstrumentAmount.amount to a number and assert > 0) so leading zeros are
accepted, and apply the same change to the identical validation rules in
current_account_events.proto and the other matching blocks (the other two
identical rules noted in the comment).

In `@services/current-account/service/grpc_control_endpoints.go`:
- Around line 347-350: The closingBalance builder hardcodes Version: 1 which
mis-serializes non-v1 instruments; replace the literal with the domain amount’s
actual version (e.g. use account.Balance().Version() or the equivalent accessor
on the domain Amount) or call the shared amount-to-proto helper used in
services/financial-accounting/service/adapters.go so the
InstrumentAmount.Version is taken from account.Balance() instead of being
hardcoded.

---

Outside diff comments:
In `@api/proto/meridian/events/v1/financial_accounting_events_test.go`:
- Around line 702-755:
TestLedgerPostingCapturedEvent_AmountValidationDocumentation currently only
round-trips the proto via proto.Marshal/proto.Unmarshal and never runs the
CEL/protovalidate checks nor asserts expectValid; create a protovalidate
validator (protovalidate.New()) in the test, call validator.Validate(event)
inside the loop, and replace the current serialization-only assertion with
checks that if tt.expectValid then err==nil else err!=nil, failing the test
appropriately; ensure you add the protovalidate import and run validation
against the same event struct (LedgerPostingCapturedEvent with PostingAmount)
before or instead of the proto.Marshal/unmarshal assertions.

---

Nitpick comments:
In `@api/proto/meridian/events/v1/financial_accounting_events_test.go`:
- Around line 92-93: The tests currently compare only PostingAmount.Amount (and
similar single subfields) on decoded vs event, which can miss regressions in
other nested fields; update the assertions to compare the full nested
InstrumentAmount messages (e.g., decoded.PostingAmount vs event.PostingAmount)
using a deep/protobuf-aware equality check (such as reflect.DeepEqual or
proto.Equal) so InstrumentCode, Version and sibling fields like
TotalCredits/PreviousAmount are validated as well; apply the same change to the
other occurrences noted (the checks around lines for PostingAmount,
TotalCredits, PreviousAmount).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6e39f581-2e75-4683-9b17-e602f569c64b

📥 Commits

Reviewing files that changed from the base of the PR and between 69a34f1 and bc0aac6.

⛔ Files ignored due to path filters (4)
  • api/proto/meridian/events/v1/current_account_events.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
  • api/proto/meridian/events/v1/financial_accounting_events.pb.go is excluded by !**/*.pb.go, !**/*.pb.go
  • frontend/src/api/gen/meridian/events/v1/current_account_events_pb.ts is excluded by !**/gen/**
  • frontend/src/api/gen/meridian/events/v1/financial_accounting_events_pb.ts is excluded by !**/gen/**
📒 Files selected for processing (5)
  • api/proto/meridian/events/v1/current_account_events.proto
  • api/proto/meridian/events/v1/financial_accounting_events.proto
  • api/proto/meridian/events/v1/financial_accounting_events_test.go
  • services/current-account/service/grpc_control_endpoints.go
  • services/financial-accounting/service/grpc_mappers.go

Comment thread api/proto/meridian/events/v1/financial_accounting_events.proto
Comment thread services/current-account/service/grpc_control_endpoints.go 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 (1)
tests/proto/serialization_test.go (1)

119-122: Migration to InstrumentAmount looks correct.

The change from money.Money to quantityv1.InstrumentAmount with string-based Amount and InstrumentCode aligns with the PR's proto migration.

Consider adding assertions for the PostingAmount fields after deserialization (similar to lines 99-110 in the "LedgerPosting message serialization" test) to fully validate the round-trip:

🔧 Optional: Add assertions for PostingAmount fields
 		if decoded.PostingId != original.PostingId {
 			t.Errorf("posting_id mismatch: got %v, want %v", decoded.PostingId, original.PostingId)
 		}
+		if decoded.PostingAmount == nil {
+			t.Fatal("posting_amount mismatch: got nil, want non-nil")
+		}
+		if decoded.PostingAmount.Amount != original.PostingAmount.Amount {
+			t.Errorf("posting_amount.amount mismatch: got %v, want %v", decoded.PostingAmount.Amount, original.PostingAmount.Amount)
+		}
+		if decoded.PostingAmount.InstrumentCode != original.PostingAmount.InstrumentCode {
+			t.Errorf("posting_amount.instrument_code mismatch: got %v, want %v", decoded.PostingAmount.InstrumentCode, original.PostingAmount.InstrumentCode)
+		}
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/proto/serialization_test.go` around lines 119 - 122, Add explicit
assertions that validate the deserialized PostingAmount fields: after
deserializing the LedgerPosting in the "LedgerPosting message serialization"
test, assert that PostingAmount is non-nil and that PostingAmount.Amount equals
"1000.00" and PostingAmount.InstrumentCode equals "GBP" (referencing the
quantityv1.InstrumentAmount and PostingAmount fields used when constructing the
message) so the round-trip serialization test fully verifies these values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/proto/serialization_test.go`:
- Around line 119-122: Add explicit assertions that validate the deserialized
PostingAmount fields: after deserializing the LedgerPosting in the
"LedgerPosting message serialization" test, assert that PostingAmount is non-nil
and that PostingAmount.Amount equals "1000.00" and PostingAmount.InstrumentCode
equals "GBP" (referencing the quantityv1.InstrumentAmount and PostingAmount
fields used when constructing the message) so the round-trip serialization test
fully verifies these values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ecf1e2f-e061-4d94-954d-c76abc7006a9

📥 Commits

Reviewing files that changed from the base of the PR and between bc0aac6 and 54d4626.

📒 Files selected for processing (1)
  • tests/proto/serialization_test.go

@bjcoombs bjcoombs dismissed coderabbitai[bot]’s stale review April 3, 2026 18:03

Stale bot review - all threads resolved

The previous regex rejected valid positive values like "01" and "0001.50"
that the base InstrumentAmount pattern accepts. Use a pattern that checks
for any numeric string that is not all-zeros.
bjcoombs added 2 commits April 3, 2026 19:05
Commits 700c73b and 736b769 modified proto definitions but the
generated .pb.go files were not regenerated. Running buf generate
to bring them in sync.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM. Clean migration with correct domain constraints. See summary comment for details.

Proto validation requires version >= 1. Domain instruments created
before versioning was introduced have version 0 as default.
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

See summary comment for full review. One non-blocking suggestion inline; the version-guard suggestion for adapters.go is in the summary.

Comment thread services/financial-accounting/service/grpc_mappers.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@bjcoombs bjcoombs merged commit 48f1120 into develop Apr 3, 2026
45 checks passed
@bjcoombs bjcoombs deleted the 059-asset-agnostic-posting-layer--2-6--event-proto-migration branch April 3, 2026 18:43
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.

1 participant