Skip to content

refactor: split get*subscription calls#4745

Open
turip wants to merge 3 commits into
mainfrom
feat/legacy-split-line-engine
Open

refactor: split get*subscription calls#4745
turip wants to merge 3 commits into
mainfrom
feat/legacy-split-line-engine

Conversation

@turip

@turip turip commented Jul 18, 2026

Copy link
Copy Markdown
Member

Summary

  • split the combined GetLinesForSubscription API into typed standard-line, gathering-line, and split-line-group service/adapter methods
  • update subscription sync to assemble its persisted-state items from those typed results
  • remove the billing-owned LineOrHierarchy union and the obsolete downstream conversion helpers

Motivation

This establishes the ownership boundary needed to move split-line-group handling into the legacy billing line engine. Standard lines, gathering lines, and split-line hierarchies can now be loaded independently, so the main billing path no longer needs a combined line-or-hierarchy abstraction and split-line-group behavior can move behind the legacy engine incrementally.

Validation

  • nix develop --impure .#ci -c make test-nocache
    • 6,156 tests passed
    • 9 skipped

Summary by CodeRabbit

  • New Features

    • Added dedicated retrieval for standard invoice lines, gathering invoice lines, and split-line hierarchies during subscription synchronization.
    • Improved progressive billing calculations and split-line group handling.
    • Added clearer invoice and line header data for integration workflows.
  • Bug Fixes

    • Preserved split-line hierarchy metadata during flat-fee rating.
    • Improved handling of deleted lines and invoices during synchronization and billing calculations.

Greptile Summary

This PR refactors the combined GetLinesForSubscription API into three typed adapter/service methods (GetStandardLinesForSubscription, GetGatheringLinesForSubscription, GetSplitLineGroupsForSubscription), moves split-line-group domain types into a new billing/invoicing/legacy/splitlinegroup package, and removes the billing.LineOrHierarchy union type along with the downstream GetLinesForSubscription conversion helpers. The architectural direction is sound — cleanly typed retrieval methods and isolated domain ownership — but the migration is incomplete in several files, resulting in compile errors and a broken initialization path.

  • The new splitlinegroup/adapter/invoicelinesplitgroup.go and the patchinvoicelinehierarchy.go AddShrink/AddExtend methods still reference billing.SplitLineHierarchy, billing.LineWithInvoiceHeader, and billing.InvoiceSplitLineGroupAdapter, all of which were deleted from the billing package in this same PR.
  • billing/rating/service/testutil/ubptest.go was updated to use billing.StandardLineWithSplitLineHierarchy (defined in lineengine, not billing) and several other deleted billing.* hierarchy types.
  • invoiceupdater.Config now requires SplitLineGroupService but the field is never threaded through reconciler.Configinvoiceupdater.New, so service initialization always returns an error at startup.

Confidence Score: 1/5

Not safe to merge: the PR introduces compile errors in at least three packages and a runtime initialization failure in the subscription-sync reconciler.

Three packages fail to compile because deleted billing.* types are still referenced after the migration. Separately, the reconciler can never be constructed at runtime because SplitLineGroupService is a required field in invoiceupdater.Config but is not wired through any of the calling configs. The overall refactoring direction is well-designed, but these gaps need to be resolved before the code can run at all.

patchinvoicelinehierarchy.go (AddShrink loop body + AddExtend entirely unupdated), splitlinegroup/adapter/invoicelinesplitgroup.go (all billing.* hierarchy type references), ubptest.go (billing.StandardLineWithSplitLineHierarchy and other deleted types), reconciler.go (missing SplitLineGroupService in invoiceupdater.New call).

Important Files Changed

Filename Overview
openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go AddShrink partially updated (range changed but loop body still uses deleted billing.LineWithInvoiceHeader fields); AddExtend not updated at all — both methods reference types/fields deleted by this PR, causing compile errors.
openmeter/billing/invoicing/legacy/splitlinegroup/adapter/invoicelinesplitgroup.go New adapter file added, but references billing.InvoiceSplitLineGroupAdapter, billing.SplitLineHierarchy, and billing.LineWithInvoiceHeader — all deleted from the billing package in this same PR — causing compile errors.
openmeter/billing/rating/service/testutil/ubptest.go Updated to use billing.StandardLineWithSplitLineHierarchy (defined in lineengine, not billing) and other deleted billing types; fails to compile.
openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go SplitLineGroupService wired into invoiceupdater.Config but never supplied by reconciler.Config; invoiceupdater.New always fails at validation, preventing any successful service construction.
openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go Cleanly split into three independent typed queries (standard, gathering, split-line-groups); normalization functions simplified; Billing Customer Lock guards against concurrent status transitions.
openmeter/billing/adapter/gatheringlines.go New GetGatheringLinesForSubscription adapter method added; filters by gathering status and handles schema-level lookup correctly.
openmeter/billing/adapter/stdinvoicelines.go GetLinesForSubscription replaced by GetStandardLinesForSubscription; split-line-group query and old gathering logic removed; type narrowing now handled at query level.
openmeter/billing/lineengine/stdline.go New StandardLineWithSplitLineHierarchy wrapper correctly embeds *billing.StandardLine with splitlinegroup.SplitLineHierarchy; progressive billing methods moved here from billing.StandardLine.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant SS as SubscriptionSync
    participant L as persistedstate.Loader
    participant BS as billing.Service
    participant SLG as splitlinegroup.Service
    participant R as reconciler
    participant IU as invoiceupdater.Updater

    SS->>L: LoadForSubscription(sub)
    L->>BS: GetStandardLinesForSubscription()
    L->>BS: GetGatheringLinesForSubscription()
    L->>SLG: GetSplitLineGroupsForSubscription()
    L-->>SS: "State{lineItems, hierarchyItems, byUniqueID}"

    SS->>R: Apply(plan)
    R->>IU: ApplyPatches(patches)
    IU->>SLG: DeleteSplitLineGroup() / UpdateSplitLineGroup()
    IU->>BS: (standard invoice line ops)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant SS as SubscriptionSync
    participant L as persistedstate.Loader
    participant BS as billing.Service
    participant SLG as splitlinegroup.Service
    participant R as reconciler
    participant IU as invoiceupdater.Updater

    SS->>L: LoadForSubscription(sub)
    L->>BS: GetStandardLinesForSubscription()
    L->>BS: GetGatheringLinesForSubscription()
    L->>SLG: GetSplitLineGroupsForSubscription()
    L-->>SS: "State{lineItems, hierarchyItems, byUniqueID}"

    SS->>R: Apply(plan)
    R->>IU: ApplyPatches(patches)
    IU->>SLG: DeleteSplitLineGroup() / UpdateSplitLineGroup()
    IU->>BS: (standard invoice line ops)
Loading

Comments Outside Diff (2)

  1. openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go, line 90-133 (link)

    P0 Incomplete type migration breaks AddShrink and AddExtend

    existingHierarchy.Lines() now returns []splitlinegroup.LineHeaderAccessor, but the loop body throughout AddShrink (line 95 onwards) still accesses child.Line.* and child.Invoice.* — fields that do not exist on the LineHeaderAccessor interface. This is a compilation error on every affected statement (child.Line.GetServicePeriod(), child.Line.CloneWithoutChildren(), child.Invoice.AsInvoice().Type(), etc.).

    AddExtend was not updated at all: line 166 does len(existingHierarchy.Lines) where Lines is now a method value (a function), which cannot be passed to len, and line 168 references billing.LineWithInvoiceHeader which was deleted from the billing package. Both are compile errors that block any build of this package.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go
    Line: 90-133
    
    Comment:
    **Incomplete type migration breaks `AddShrink` and `AddExtend`**
    
    `existingHierarchy.Lines()` now returns `[]splitlinegroup.LineHeaderAccessor`, but the loop body throughout `AddShrink` (line 95 onwards) still accesses `child.Line.*` and `child.Invoice.*` — fields that do not exist on the `LineHeaderAccessor` interface. This is a compilation error on every affected statement (`child.Line.GetServicePeriod()`, `child.Line.CloneWithoutChildren()`, `child.Invoice.AsInvoice().Type()`, etc.).
    
    `AddExtend` was not updated at all: line 166 does `len(existingHierarchy.Lines)` where `Lines` is now a method value (a function), which cannot be passed to `len`, and line 168 references `billing.LineWithInvoiceHeader` which was deleted from the billing package. Both are compile errors that block any build of this package.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Codex

  2. openmeter/billing/rating/service/testutil/ubptest.go, line 62-103 (link)

    P0 Test utility references multiple deleted and mislocated billing types

    This file was updated to use billing.StandardLineWithSplitLineHierarchy (line 62) and a set of hierarchy types (lines 79–103), but none of these exist in the billing package after this PR:

    • billing.StandardLineWithSplitLineHierarchy — defined in lineengine.StandardLineWithSplitLineHierarchy, not exported through billing
    • billing.SplitLineHierarchy — deleted from billing package (moved to splitlinegroup)
    • billing.SplitLineGroup / billing.SplitLineGroupMutableFields — deleted
    • billing.LineWithInvoiceHeader / billing.NewLineWithInvoiceHeader — deleted

    Every reference to these types is a compilation error. The import would need to be updated to billinglineengine or splitlinegroup depending on which concrete type the test should use.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: openmeter/billing/rating/service/testutil/ubptest.go
    Line: 62-103
    
    Comment:
    **Test utility references multiple deleted and mislocated billing types**
    
    This file was updated to use `billing.StandardLineWithSplitLineHierarchy` (line 62) and a set of hierarchy types (lines 79–103), but none of these exist in the `billing` package after this PR:
    
    - `billing.StandardLineWithSplitLineHierarchy` — defined in `lineengine.StandardLineWithSplitLineHierarchy`, not exported through `billing`
    - `billing.SplitLineHierarchy` — deleted from billing package (moved to `splitlinegroup`)
    - `billing.SplitLineGroup` / `billing.SplitLineGroupMutableFields` — deleted
    - `billing.LineWithInvoiceHeader` / `billing.NewLineWithInvoiceHeader` — deleted
    
    Every reference to these types is a compilation error. The import would need to be updated to `billinglineengine` or `splitlinegroup` depending on which concrete type the test should use.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Codex

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go:90-133
**Incomplete type migration breaks `AddShrink` and `AddExtend`**

`existingHierarchy.Lines()` now returns `[]splitlinegroup.LineHeaderAccessor`, but the loop body throughout `AddShrink` (line 95 onwards) still accesses `child.Line.*` and `child.Invoice.*` — fields that do not exist on the `LineHeaderAccessor` interface. This is a compilation error on every affected statement (`child.Line.GetServicePeriod()`, `child.Line.CloneWithoutChildren()`, `child.Invoice.AsInvoice().Type()`, etc.).

`AddExtend` was not updated at all: line 166 does `len(existingHierarchy.Lines)` where `Lines` is now a method value (a function), which cannot be passed to `len`, and line 168 references `billing.LineWithInvoiceHeader` which was deleted from the billing package. Both are compile errors that block any build of this package.

### Issue 2 of 4
openmeter/billing/invoicing/legacy/splitlinegroup/adapter/invoicelinesplitgroup.go:19-66
**New adapter file references deleted billing types**

This file was added in the PR but references three types that were simultaneously deleted from the `billing` package by removing `billing/invoicelinesplitgroup.go`:

- Line 19: `billing.InvoiceSplitLineGroupAdapter` — interface deleted from `billing/adapter.go`
- Line 21+: `billing.SplitLineHierarchy` — type deleted from billing package
- Line 268+: `billing.LineWithInvoiceHeader` — type deleted from billing package

The compile-time interface check `var _ billing.InvoiceSplitLineGroupAdapter = (*adapter)(nil)` and all subsequent method signatures and return-type literals are compilation errors. The adapter appears to need updating to use the newly defined types in the `splitlinegroup` package instead.

### Issue 3 of 4
openmeter/billing/rating/service/testutil/ubptest.go:62-103
**Test utility references multiple deleted and mislocated billing types**

This file was updated to use `billing.StandardLineWithSplitLineHierarchy` (line 62) and a set of hierarchy types (lines 79–103), but none of these exist in the `billing` package after this PR:

- `billing.StandardLineWithSplitLineHierarchy` — defined in `lineengine.StandardLineWithSplitLineHierarchy`, not exported through `billing`
- `billing.SplitLineHierarchy` — deleted from billing package (moved to `splitlinegroup`)
- `billing.SplitLineGroup` / `billing.SplitLineGroupMutableFields` — deleted
- `billing.LineWithInvoiceHeader` / `billing.NewLineWithInvoiceHeader` — deleted

Every reference to these types is a compilation error. The import would need to be updated to `billinglineengine` or `splitlinegroup` depending on which concrete type the test should use.

### Issue 4 of 4
openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go:79-86
**`SplitLineGroupService` never provided to `invoiceupdater.New`, so initialization always fails**

`invoiceupdater.Config.Validate()` requires `SplitLineGroupService` to be non-nil and returns an error when it is absent. The call here omits it — the `reconciler.Config` struct also has no `SplitLineGroupService` field — so `invoiceupdater.New` will always return `"split line group service is required"`, meaning the reconciler itself can never be constructed successfully.

Previously `billingService.DeleteSplitLineGroup` / `billingService.UpdateSplitLineGroup` were called directly; those methods were removed from `billing.Service` in this PR and transferred to `splitlinegroup.Service`. The service needs to be wired through `reconciler.Config` and `service.Config` down to the `invoiceupdater.New` call.

Reviews (2): Last reviewed commit: "feat: move legacy splitline handling to ..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@turip
turip requested a review from a team as a code owner July 18, 2026 06:36
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing now separates standard, gathering, and split-line-group subscription retrieval. Progressive billing and quantity snapshotting move into a dedicated legacy line engine, while subscription sync and service wiring consume the new typed APIs and hierarchy models.

Changes

Billing line contracts and models

Layer / File(s) Summary
Typed retrieval contracts and hierarchy models
openmeter/billing/adapter.go, openmeter/billing/service.go, openmeter/billing/invoicing/legacy/splitlinegroup/*, openmeter/billing/stdinvoiceline.go, openmeter/billing/gatheringinvoice.go
Mixed LineOrHierarchy APIs are replaced with typed standard, gathering, and split-line-group retrieval contracts. Split-line hierarchies and header accessors move into the legacy package.
Subscription retrieval implementations
openmeter/billing/adapter/*, openmeter/billing/service/*
Adapters and services query and return standard lines, gathering lines, and split-line hierarchies independently, with validation and transactional execution.
Legacy line engine and rating flow
openmeter/billing/lineengine/*, openmeter/billing/rating/*, openmeter/billing/charges/*
The line engine resolves hierarchy headers, snapshots quantities, calculates progressive detailed lines, and supplies progressive billing accessors to rating.
Subscription sync and reconciliation
openmeter/billing/worker/subscriptionsync/service/*
Persisted-state loading normalizes typed results and derives invoice IDs from persisted items. Reconciliation uses injected quantity snapshotting and split-line-group services.
Application wiring and test environments
app/common/billing.go, openmeter/billing/service/*, test/*, openmeter/server/server_test.go
A legacy billing line engine is constructed, registered, stored in the billing registry, and passed to subscription synchronization and test services.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: tothandras, rolosp

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main refactor: splitting the subscription line retrieval API into typed methods.
✨ 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 feat/legacy-split-line-engine

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.

@turip turip added release-note/misc Miscellaneous changes area/billing labels Jul 18, 2026
Comment on lines 19 to +66
var _ billing.InvoiceSplitLineGroupAdapter = (*adapter)(nil)

func (a *adapter) GetSplitLineGroupsForSubscription(ctx context.Context, in billing.GetLinesForSubscriptionInput) ([]billing.SplitLineHierarchy, error) {
if err := in.Validate(); err != nil {
return nil, billing.ValidationError{
Err: err,
}
}

return entutils.TransactingRepo(ctx, a, func(ctx context.Context, tx *adapter) ([]billing.SplitLineHierarchy, error) {
dbGroups, err := tx.db.BillingInvoiceSplitLineGroup.Query().
Where(billinginvoicesplitlinegroup.Namespace(in.Namespace)).
Where(billinginvoicesplitlinegroup.SubscriptionID(in.SubscriptionID)).
WithBillingInvoiceLines(func(q *db.BillingInvoiceLineQuery) {
tx.expandLineItems(q)
q.WithBillingInvoice(func(q *db.BillingInvoiceQuery) {
q.WithBillingWorkflowConfig(workflowConfigWithTaxCode)
})
}).
Where(billinginvoicesplitlinegroup.DeletedAtIsNil()).
All(ctx)
if err != nil {
return nil, fmt.Errorf("fetching split line groups: %w", err)
}

groups, err := slicesx.MapWithErr(dbGroups, func(dbGroup *db.BillingInvoiceSplitLineGroup) (billing.SplitLineHierarchy, error) {
group, err := tx.mapSplitLineGroupFromDB(dbGroup)
if err != nil {
return billing.SplitLineHierarchy{}, err
}

lines, err := tx.mapSplitLineHierarchyLinesFromDB(ctx, dbGroup.Edges.BillingInvoiceLines)
if err != nil {
return billing.SplitLineHierarchy{}, err
}

return billing.SplitLineHierarchy{
Group: group,
Lines: lines,
}, nil
})
if err != nil {
return nil, fmt.Errorf("mapping split line groups: %w", err)
}

return groups, nil
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 New adapter file references deleted billing types

This file was added in the PR but references three types that were simultaneously deleted from the billing package by removing billing/invoicelinesplitgroup.go:

  • Line 19: billing.InvoiceSplitLineGroupAdapter — interface deleted from billing/adapter.go
  • Line 21+: billing.SplitLineHierarchy — type deleted from billing package
  • Line 268+: billing.LineWithInvoiceHeader — type deleted from billing package

The compile-time interface check var _ billing.InvoiceSplitLineGroupAdapter = (*adapter)(nil) and all subsequent method signatures and return-type literals are compilation errors. The adapter appears to need updating to use the newly defined types in the splitlinegroup package instead.

Prompt To Fix With AI
This is a comment left during a code review.
Path: openmeter/billing/invoicing/legacy/splitlinegroup/adapter/invoicelinesplitgroup.go
Line: 19-66

Comment:
**New adapter file references deleted billing types**

This file was added in the PR but references three types that were simultaneously deleted from the `billing` package by removing `billing/invoicelinesplitgroup.go`:

- Line 19: `billing.InvoiceSplitLineGroupAdapter` — interface deleted from `billing/adapter.go`
- Line 21+: `billing.SplitLineHierarchy` — type deleted from billing package
- Line 268+: `billing.LineWithInvoiceHeader` — type deleted from billing package

The compile-time interface check `var _ billing.InvoiceSplitLineGroupAdapter = (*adapter)(nil)` and all subsequent method signatures and return-type literals are compilation errors. The adapter appears to need updating to use the newly defined types in the `splitlinegroup` package instead.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

Comment on lines +79 to +86
invoiceUpdater, err := invoiceupdater.New(invoiceupdater.Config{
BillingService: config.BillingService,
QuantitySnapshotter: config.LegacyBillingLineEngine,
Logger: config.Logger,
})
if err != nil {
return nil, fmt.Errorf("creating invoice updater: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P0 SplitLineGroupService never provided to invoiceupdater.New, so initialization always fails

invoiceupdater.Config.Validate() requires SplitLineGroupService to be non-nil and returns an error when it is absent. The call here omits it — the reconciler.Config struct also has no SplitLineGroupService field — so invoiceupdater.New will always return "split line group service is required", meaning the reconciler itself can never be constructed successfully.

Previously billingService.DeleteSplitLineGroup / billingService.UpdateSplitLineGroup were called directly; those methods were removed from billing.Service in this PR and transferred to splitlinegroup.Service. The service needs to be wired through reconciler.Config and service.Config down to the invoiceupdater.New call.

Prompt To Fix With AI
This is a comment left during a code review.
Path: openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go
Line: 79-86

Comment:
**`SplitLineGroupService` never provided to `invoiceupdater.New`, so initialization always fails**

`invoiceupdater.Config.Validate()` requires `SplitLineGroupService` to be non-nil and returns an error when it is absent. The call here omits it — the `reconciler.Config` struct also has no `SplitLineGroupService` field — so `invoiceupdater.New` will always return `"split line group service is required"`, meaning the reconciler itself can never be constructed successfully.

Previously `billingService.DeleteSplitLineGroup` / `billingService.UpdateSplitLineGroup` were called directly; those methods were removed from `billing.Service` in this PR and transferred to `splitlinegroup.Service`. The service needs to be wired through `reconciler.Config` and `service.Config` down to the `invoiceupdater.New` call.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go (1)

90-133: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

AddShrink still uses the old nested child shape
In patchinvoicelinehierarchy.go:90-133, existingHierarchy.Lines() now returns []LineHeaderAccessor, so child.Line / child.Invoice no longer match this loop. The shrink path needs the flat accessors here too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go`
around lines 90 - 133, The AddShrink loop over existingHierarchy.Lines() still
uses the old nested child shape. Update the loop in the shrink path to use the
returned LineHeaderAccessor directly, replacing child.Line and child.Invoice
references with the corresponding flat accessor methods while preserving the
existing delete, clone, update, and invoice-type behavior.
🧹 Nitpick comments (3)
openmeter/billing/invoicing/legacy/splitlinegroup/headers.go (1)

66-74: 🗄️ Data Integrity & Integration | 🔵 Trivial

Clone()/CloneOrNil() leave Subscription shared, not copied.

Both clones deep-copy Annotations but skip the Subscription *billing.SubscriptionReference pointer, so the "cloned" line still points at the same SubscriptionReference as the original. persistedstate/loader.go's normalizePersistedSplitLineHierarchy then mutates that shared pointer in place (ref.BillingPeriod = ...), which defeats the point of cloning before mutating. Low risk today since the pre-clone value isn't reused afterward in that call site, but it's a footgun for future refactors. See consolidated comment for the paired fix.

♻️ Deep-clone Subscription too
 func (l StandardLine) Clone() (StandardLine, error) {
 	var err error
 	l.Annotations, err = l.Annotations.Clone()
 	if err != nil {
 		return StandardLine{}, fmt.Errorf("cloning annotations: %w", err)
 	}
+
+	if l.Subscription != nil {
+		l.Subscription = l.Subscription.Clone()
+	}

 	return l, nil
 }

Also applies to: 118-132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/invoicing/legacy/splitlinegroup/headers.go` around lines 66
- 74, Update StandardLine.Clone and the corresponding CloneOrNil path to
deep-copy the Subscription *billing.SubscriptionReference instead of retaining
the original pointer. Preserve nil subscriptions as nil, and return the cloned
line with independent Subscription and Annotations values.
openmeter/billing/lineengine/engine.go (1)

29-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Validate() methods keep returning on the first failure instead of aggregating. Each of these files adds new nil-checks to a Config.Validate() that still returns on the first error found, rather than collecting all failures and returning models.NewNillableGenericValidationError(errors.Join(errs...)) as required by the guideline. Same root cause across the cohort — the pre-existing early-return style in these functions, now extended with new checks.

  • openmeter/billing/lineengine/engine.go#L29-L51: rewrite Config.Validate() to accumulate errs for SplitLineGroupAdapter/RatingService/FeatureService/StreamingConnector/MaxParallelQuantitySnapshots and return models.NewNillableGenericValidationError(errors.Join(errs...)).
  • openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go#L45-L48: fold the new LegacyBillingLineEngine nil-check into an aggregated-errors Validate().
  • openmeter/billing/worker/subscriptionsync/service/service.go#L48-L51: fold the new LegacyBillingLineEngine nil-check into an aggregated-errors Validate().
  • openmeter/billing/service/service.go#L88-L91: fold the new LegacyBillingLineEngine nil-check into an aggregated-errors Validate().

Based on learnings from the coding guidelines: "For Validate() error methods, collect all validation failures and return models.NewNillableGenericValidationError(errors.Join(errs...)), preserving field context with wrapped errors."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/lineengine/engine.go` around lines 29 - 51, Update each
Config.Validate method to collect all validation failures in errs instead of
returning early, preserving field context with wrapped errors, then return
models.NewNillableGenericValidationError(errors.Join(errs...)). Apply this to
openmeter/billing/lineengine/engine.go lines 29-51 for all five checks, and fold
the LegacyBillingLineEngine nil-check into the aggregated validation in
openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go lines
45-48, openmeter/billing/worker/subscriptionsync/service/service.go lines 48-51,
and openmeter/billing/service/service.go lines 88-91.

Source: Coding guidelines

openmeter/billing/lineengine/quantitysnapshot.go (1)

234-240: 🩺 Stability & Availability | 🔵 Trivial

Heads up: there's a TODO[BeforeMerge] sitting right on the new validation branch.

The comment on line 236 flags that this new "split line group id set but hierarchy not expanded" check needs a test before merging. Since this is exactly that moment, want me to sketch a test for getFeatureUsageInput.Validate() covering this branch, or would you rather track it as a follow-up issue?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/lineengine/quantitysnapshot.go` around lines 234 - 240, Add
a test for getFeatureUsageInput.Validate() that supplies a line with
SplitLineGroupID set and SplitLineHierarchy unset, and asserts the validation
returns the expected error. Remove the TODO[BeforeMerge] comment after the
branch is covered, preserving existing validation behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go`:
- Around line 349-352: Update stubRatingService.GenerateDetailedLines to return
the configured s.result after recording the options, instead of constructing an
empty billingrating.GenerateDetailedLinesResult, so tests initializing
stubRatingService with a result receive their configured detailed lines.

In `@openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go`:
- Line 43: Update every affected Validate method in
openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go at
lines 43, 98, 118, 150, and 293 to return
models.NewNillableGenericValidationError(errors.Join(errs...)). In the methods
at lines 43, 98, 118, and 150, wrap nested validation errors with descriptive
field context using wrapped errors before appending them; the line 293 site
requires only the final error wrapper.

In `@openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go`:
- Around line 333-352: Update StandardLine.Clone() and
GatheringLine.CloneOrNil() to deep-copy the Subscription field, not only
Annotations, before returning their clones. This ensures
normalizePersistedSplitLineHierarchy can call normalizeSubscriptionReference
without mutating the original SubscriptionReference through shared pointers.
- Around line 26-28: Update the NewLoader call in the reconciliation setup to
pass the available splitLineGroupService implementation as the third dependency,
alongside billingService and chargesService. Ensure the constructor invocation
matches the expanded splitLineGroupService interface and preserves the existing
dependency wiring.

---

Outside diff comments:
In
`@openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go`:
- Around line 90-133: The AddShrink loop over existingHierarchy.Lines() still
uses the old nested child shape. Update the loop in the shrink path to use the
returned LineHeaderAccessor directly, replacing child.Line and child.Invoice
references with the corresponding flat accessor methods while preserving the
existing delete, clone, update, and invoice-type behavior.

---

Nitpick comments:
In `@openmeter/billing/invoicing/legacy/splitlinegroup/headers.go`:
- Around line 66-74: Update StandardLine.Clone and the corresponding CloneOrNil
path to deep-copy the Subscription *billing.SubscriptionReference instead of
retaining the original pointer. Preserve nil subscriptions as nil, and return
the cloned line with independent Subscription and Annotations values.

In `@openmeter/billing/lineengine/engine.go`:
- Around line 29-51: Update each Config.Validate method to collect all
validation failures in errs instead of returning early, preserving field context
with wrapped errors, then return
models.NewNillableGenericValidationError(errors.Join(errs...)). Apply this to
openmeter/billing/lineengine/engine.go lines 29-51 for all five checks, and fold
the LegacyBillingLineEngine nil-check into the aggregated validation in
openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go lines
45-48, openmeter/billing/worker/subscriptionsync/service/service.go lines 48-51,
and openmeter/billing/service/service.go lines 88-91.

In `@openmeter/billing/lineengine/quantitysnapshot.go`:
- Around line 234-240: Add a test for getFeatureUsageInput.Validate() that
supplies a line with SplitLineGroupID set and SplitLineHierarchy unset, and
asserts the validation returns the expected error. Remove the TODO[BeforeMerge]
comment after the branch is covered, preserving existing validation behavior.
🪄 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: da4ceb26-1071-4b04-9367-e3d5629da650

📥 Commits

Reviewing files that changed from the base of the PR and between 97279e3 and 8b49204.

📒 Files selected for processing (48)
  • app/common/billing.go
  • openmeter/billing/adapter.go
  • openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go
  • openmeter/billing/charges/usagebased/rating.go
  • openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go
  • openmeter/billing/gatheringinvoice.go
  • openmeter/billing/invoicelinesplitgroup.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/adapter.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/adapter/invoicelinesplitgroup.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/headers.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup_test.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/service.go
  • openmeter/billing/invoicing/legacy/splitlinegroup/service/invoicelinesplitgroup.go
  • openmeter/billing/lineengine/engine.go
  • openmeter/billing/lineengine/quantitysnapshot.go
  • openmeter/billing/lineengine/splitlinegroup.go
  • openmeter/billing/lineengine/stdinvoice.go
  • openmeter/billing/lineengine/stdline.go
  • openmeter/billing/rating/line.go
  • openmeter/billing/rating/service.go
  • openmeter/billing/rating/service/detailedline.go
  • openmeter/billing/rating/service/mutator/credits_test.go
  • openmeter/billing/rating/service/mutator/forbidunitconfig_test.go
  • openmeter/billing/rating/service/mutator/unitconfig_test.go
  • openmeter/billing/rating/service/rate/types.go
  • openmeter/billing/rating/service/testutil/ubptest.go
  • openmeter/billing/service.go
  • openmeter/billing/service/service.go
  • openmeter/billing/service/stdinvoiceline.go
  • openmeter/billing/stdinvoiceedit.go
  • openmeter/billing/stdinvoiceline.go
  • openmeter/billing/worker/subscriptionsync/service/base_test.go
  • openmeter/billing/worker/subscriptionsync/service/persistedstate/item.go
  • openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/invoiceupdater/invoiceupdate.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/invoiceupdater/patch.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/patchhelpers.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/patchinvoicelinehierarchy.go
  • openmeter/billing/worker/subscriptionsync/service/reconciler/reconciler.go
  • openmeter/billing/worker/subscriptionsync/service/service.go
  • openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go
  • openmeter/server/server_test.go
  • test/app/testenv.go
  • test/billing/subscription_test.go
  • test/billing/suite.go
  • test/customer/testenv.go
  • test/subscription/framework_test.go
💤 Files with no reviewable changes (8)
  • openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go
  • openmeter/billing/service/stdinvoiceline.go
  • openmeter/billing/charges/usagebased/rating.go
  • openmeter/billing/invoicelinesplitgroup.go
  • openmeter/server/server_test.go
  • openmeter/billing/stdinvoiceedit.go
  • openmeter/billing/adapter.go
  • openmeter/billing/service.go

Comment on lines 349 to 352
func (s *stubRatingService) GenerateDetailedLines(in billingrating.StandardLineAccessor, opts ...billingrating.GenerateDetailedLinesOption) (billingrating.GenerateDetailedLinesResult, error) {
s.lastOpts = billingrating.NewGenerateDetailedLinesOptions(opts...)
return s.result, nil
return billingrating.GenerateDetailedLinesResult{}, nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'stubRatingService\{' -A5 openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go
rg -n '\.result\b' openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go

Repository: openmeterio/openmeter

Length of output: 1430


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go
printf '\n----\n'
sed -n '220,420p' openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go

Repository: openmeterio/openmeter

Length of output: 12681


Return the configured stub result — the tests in this file still initialize stubRatingService{result: ...}, so GenerateDetailedLines should return s.result instead of an empty value; otherwise those cases will always get zeroed detailed lines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/charges/usagebased/service/rating/delta/engine_test.go`
around lines 349 - 352, Update stubRatingService.GenerateDetailedLines to return
the configured s.result after recording the options, instead of constructing an
empty billingrating.GenerateDetailedLinesResult, so tests initializing
stubRatingService with a result receive their configured detailed lines.

errs = append(errs, err)
}

return errors.Join(errs...)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Wrap validation errors and preserve field context.

The Validate methods currently collect errors but fail to wrap the final joined error in models.NewNillableGenericValidationError and omit field context for nested validations. As per coding guidelines for **/*.go, validation methods must return models.NewNillableGenericValidationError(errors.Join(errs...)) and preserve field context with wrapped errors to ensure errors are mapped correctly in the API layer.

  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L43-L43: return models.NewNillableGenericValidationError(errors.Join(errs...)) and wrap nested validation calls above with field context (e.g., fmt.Errorf("service period: %w", err)).
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L98-L98: return models.NewNillableGenericValidationError(errors.Join(errs...)) and wrap nested errors.
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L118-L118: return models.NewNillableGenericValidationError(errors.Join(errs...)) and wrap nested errors.
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L150-L150: return models.NewNillableGenericValidationError(errors.Join(errs...)) and wrap nested errors.
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L293-L293: return models.NewNillableGenericValidationError(errors.Join(errs...)).
📍 Affects 1 file
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L43-L43 (this comment)
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L98-L98
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L118-L118
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L150-L150
  • openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go#L293-L293
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go`
at line 43, Update every affected Validate method in
openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go at
lines 43, 98, 118, 150, and 293 to return
models.NewNillableGenericValidationError(errors.Join(errs...)). In the methods
at lines 43, 98, 118, and 150, wrap nested validation errors with descriptive
field context using wrapped errors before appending them; the line 293 site
requires only the final error wrapper.

Source: Coding guidelines

Comment on lines +26 to +28
type splitLineGroupService interface {
GetSplitLineGroupsForSubscription(ctx context.Context, input billing.GetLinesForSubscriptionInput) ([]splitlinegroup.SplitLineHierarchy, error)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find what implements splitLineGroupService and how it's wired into NewLoader.
rg -n 'func.*GetSplitLineGroupsForSubscription' openmeter/billing -g '*.go'
rg -n 'NewLoader\(' openmeter/billing -g '*.go' -A3

Repository: openmeterio/openmeter

Length of output: 1647


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go
printf '\n---\n'
sed -n '1,120p' openmeter/billing/invoicing/legacy/splitlinegroup/service/invoicelinesplitgroup.go
printf '\n---\n'
sed -n '1,120p' openmeter/billing/invoicing/legacy/splitlinegroup/adapter/invoicelinesplitgroup.go
printf '\n---\n'
sed -n '1,120p' openmeter/billing/worker/subscriptionsync/service/reconcile.go

Repository: openmeterio/openmeter

Length of output: 14054


Wire splitLineGroupService into NewLoader
In openmeter/billing/worker/subscriptionsync/service/reconcile.go:31, persistedstate.NewLoader is still called with only billingService and chargesService, but the constructor now requires splitLineGroupService too. That leaves the new dependency unwired and breaks the build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go`
around lines 26 - 28, Update the NewLoader call in the reconciliation setup to
pass the available splitLineGroupService implementation as the third dependency,
alongside billingService and chargesService. Ensure the constructor invocation
matches the expanded splitLineGroupService interface and preserves the existing
dependency wiring.

Comment on lines +333 to 352
func normalizePersistedSplitLineHierarchy(hierarchy splitlinegroup.SplitLineHierarchy) (*splitlinegroup.SplitLineHierarchy, error) {
cloned, err := hierarchy.Clone()
if err != nil {
return nil, fmt.Errorf("cloning hierarchy: %w", err)
}

for i := range cloned.Lines {
cloned.Lines[i].Line.UpdateServicePeriod(func(period *timeutil.ClosedPeriod) {
*period = period.Truncate(streaming.MinimumWindowSizeDuration)
})
cloned.Group.ServicePeriod = cloned.Group.ServicePeriod.Truncate(streaming.MinimumWindowSizeDuration)

if invoiceAtAccessor, ok := cloned.Lines[i].Line.(billing.InvoiceAtAccessor); ok {
invoiceAtAccessor.SetInvoiceAt(invoiceAtAccessor.GetInvoiceAt().Truncate(streaming.MinimumWindowSizeDuration))
}
for i, line := range cloned.StandardLines {
line.ServicePeriod = line.ServicePeriod.Truncate(streaming.MinimumWindowSizeDuration)
normalizeSubscriptionReference(line.Subscription)

normalizeSubscriptionReference(cloned.Lines[i].Line.GetSubscriptionReference())
}
cloned.StandardLines[i] = line
}

return billing.NewLineOrHierarchy(&cloned), nil
default:
return lineOrHierarchy, nil
if cloned.GatheringLine != nil {
cloned.GatheringLine.ServicePeriod = cloned.GatheringLine.ServicePeriod.Truncate(streaming.MinimumWindowSizeDuration)
cloned.GatheringLine.InvoiceAt = cloned.GatheringLine.InvoiceAt.Truncate(streaming.MinimumWindowSizeDuration)
normalizeSubscriptionReference(cloned.GatheringLine.Subscription)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

git ls-files | rg 'openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go|splitlinegroup|headers.go|normalizeSubscriptionReference|StandardLine'

Repository: openmeterio/openmeter

Length of output: 1190


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- outline: loader.go ---'
ast-grep outline openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go --view expanded

echo
echo '--- outline: splitlinegroup headers/service files ---'
ast-grep outline openmeter/billing/invoicing/legacy/splitlinegroup/headers.go --view expanded
echo
ast-grep outline openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go --view expanded

echo
echo '--- relevant slices: loader.go ---'
sed -n '300,380p' openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go

echo
echo '--- relevant slices: splitlinegroup/headers.go ---'
sed -n '1,260p' openmeter/billing/invoicing/legacy/splitlinegroup/headers.go

echo
echo '--- relevant slices: splitlinegroup/invoicelinesplitgroup.go ---'
sed -n '1,260p' openmeter/billing/invoicing/legacy/splitlinegroup/invoicelinesplitgroup.go

Repository: openmeterio/openmeter

Length of output: 20033


Deep-copy Subscription in the line clones
In openmeter/billing/invoicing/legacy/splitlinegroup/headers.go, StandardLine.Clone() and GatheringLine.CloneOrNil() only copy Annotations, so normalizeSubscriptionReference(...) still mutates the original SubscriptionReference through a shared pointer. Copy Subscription in the clone helpers before normalizing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/billing/worker/subscriptionsync/service/persistedstate/loader.go`
around lines 333 - 352, Update StandardLine.Clone() and
GatheringLine.CloneOrNil() to deep-copy the Subscription field, not only
Annotations, before returning their clones. This ensures
normalizePersistedSplitLineHierarchy can call normalizeSubscriptionReference
without mutating the original SubscriptionReference through shared pointers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant