You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .agents/skills/charges/SKILL.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -113,6 +113,8 @@ Do not emulate every billing rating mutator in the line mapper. Usage discounts
113
113
Detailed-line expansion rules:
114
114
115
115
-`meta.ExpandDetailedLines` is not standalone for charge reads; it requires `meta.ExpandRealizations`
116
+
- usage-based deleted realization runs are hidden from `meta.ExpandRealizations` by default because their invoice/ledger effect has been cleaned up; request `meta.ExpandDeletedRealizations` together with `meta.ExpandRealizations` only when a caller must inspect cleanup state, such as frontend audit views or deletion tests
117
+
- production code that sums or interprets realization history must explicitly skip runs with `DeletedAt != nil`, documenting the business reason at the guard, because deleted realizations are no longer effective billable history
116
118
- usage-based detailed lines live under `RealizationRun.DetailedLines`
117
119
- flat-fee detailed lines also live under `Charge.Realizations.DetailedLines`, not on the root charge
118
120
-`mo.None()` means detailed lines were not expanded; present options mean expanded data, even when the underlying slice is nil/empty
@@ -149,6 +151,18 @@ Important rules:
149
151
- usage-based invoice branches should read in this order: `started -> waiting_for_collection -> processing -> issuing -> completed`, then auto-advance out of the branch. Keep `invoice_issued` as the boundary between `processing` and `issuing`, run `FinalizeInvoiceRun(...)` from the `issuing` state, and let `completed` be the last branch-local status before `next` returns a partial invoice to `active` or moves a final invoice to `active.awaiting_payment_settlement`
150
152
- when adding or renaming usage-based detailed statuses, remember that `status_detailed` is an Ent enum for `ChargeUsageBased`; run `make generate` so the generated enum validators and migrate schema include the new values before trusting state-machine changes
151
153
154
+
Usage-based credit-then-invoice extension rules:
155
+
156
+
-`PatchExtend` must represent a real extension: the new service period end must be after the persisted intent end; full service period and billing period ends may stay unchanged but must not move backwards.
157
+
- Do not stretch an in-progress final realization run to cover the extension. There can only be one active run, and stretching the old final run would keep the run open across the extension and delay the standard invoice lifecycle.
158
+
- If the current final realization run is still backed by a mutable invoice line, the extend flow should update the charge intent, emit an invoice-line delete patch for that mutable standard line, and let the billing invoice updater/line engine delete the invoice line and mark the run deleted. The charge should move back to `active` with `AdvanceAfter` set to the new service-period end so the extended tail can realize later.
159
+
- While a mutable final invoice run is before invoice issuing (`active.final_realization.started`, `active.final_realization.waiting_for_collection`, or `active.final_realization.processing`), billing remains the owner of the ongoing invoice lifecycle. Extension may delete the mutable line, mark the current run deleted, and recreate a gathering line; a direct `AdvanceCharges(...)` before the new service-period end must be a no-op, and the replacement final run should be created by billing when the replacement gathering line is invoiced at the extended end.
160
+
- Once a final invoice run reaches `active.final_realization.issuing` or `active.final_realization.completed`, extend is explicitly rejected by `UnsupportedExtendOperation` because invoice lifecycle callbacks or state-machine advancement still own those states. Subscription sync is expected to retry instead of moving the charge out of those states manually.
161
+
- Extending from `active.awaiting_payment_settlement` is allowed: preserve the invoice line and ledger bookings, reclassify the old final run as partial, move the charge back to `active`, and create only a tail gathering line.
162
+
- If the old final realization has already passed into immutable invoice territory, keep the invoice and ledger untouched. Reclassify the old final run as a partial invoice run and move the charge back to `active`; the extended tail will produce a new final run later. Immutable invoice cleanup should be surfaced as validation warnings by billing rather than reversing ledger bookings.
163
+
- Pending gathering lines for the charge should be extended in place by charge ID. Existing standard invoice lines should only be deleted in the mutable-current-final case above.
164
+
- These rules are intentionally about not blocking the invoice train: extension should not hold a standard invoice lifecycle open while waiting for newly extended usage windows to finish.
165
+
152
166
Operational consequence:
153
167
154
168
- adding a new charge engine enum without a registered implementation causes invoice collection to fail when billing resolves the line engine
Copy file name to clipboardExpand all lines: .agents/skills/subscriptionsync/SKILL.md
+9-7Lines changed: 9 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,11 +142,11 @@ Important routing rules:
142
142
143
143
The `filterInScopeLines` function gates which target items enter reconciliation. It filters out non-billable items for every backend, and only invoicing-backed targets are additionally gated on `GetExpectedLine()`. This runs before any diffing so absent targets naturally produce delete/no-op outcomes.
This is intentional. If a test expects credit-only cancellation to fail on delete, that is current behavior, not a bug in the test.
145
+
Charge-backed mutation notes:
146
+
- charge collections support create/delete and period-shape changes
147
+
- shrink and most extend operations are emitted as an emulated replacement: delete the existing charge and create a replacement charge from the target state
148
+
- usage-based `credit_then_invoice` extend is emitted as a native charge patch so the usage-based state machine can preserve existing immutable invoice/ledger state and keep the invoice train unblocked
149
+
- charge-backed explicit prorate still returns unsupported; charge domains own their own proration/materialization behavior
150
150
151
151
## Invoice vs Charge Semantics
152
152
@@ -160,8 +160,10 @@ Keep these separate:
160
160
- Charge sync:
161
161
- provisions charge intents directly
162
162
- does not go through invoice-line rendering
163
-
- currently only create is supported
164
-
- invoice-style semantic proration is skipped in `diffItem(...)`; charges own their own proration logic
163
+
- supports create/delete and period-shape changes
164
+
- emits shrink and most extend operations as delete+create replacements
165
+
- emits usage-based `credit_then_invoice` extend as a native charge patch
166
+
- keeps explicit prorate unsupported because charges own their own proration logic
165
167
166
168
Do not force charge behavior through invoice abstractions.
logger.Info("delete gathering line by charge id patch", "charge_id", p.deleteGatheringLineByChargeIDPatch.ChargeID)
154
+
casePatchOpUpdateGatheringLineByChargeID:
155
+
logger.Info("update gathering line by charge id patch", "charge_id", p.updateGatheringLineByChargeIDPatch.ChargeID, "new_service_period_to", p.updateGatheringLineByChargeIDPatch.ServicePeriodTo)
0 commit comments