Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .kiro/specs/billing-post-usage-correctness-hardening/spec.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"feature_name": "billing-post-usage-correctness-hardening",
"created_at": "2026-08-16T15:42:00+02:00",
"updated_at": "2026-08-16T15:42:00+02:00",
"updated_at": "2026-08-16T21:08:51+02:00",
"language": "en",
"phase": "tasks-generated",
"approvals": {
"requirements": {
"generated": true,
"approved": false
"approved": true
},
"design": {
"generated": true,
"approved": false
"approved": true
},
"tasks": {
"generated": true,
"approved": false
"approved": true
}
},
"ready_for_implementation": false,
"ready_for_implementation": true,
"project_description": "Correct and harden the post-usage billing architecture merged in PR #340 without reopening the broader simplification refactor: preserve actual B-leg ordering, settle against the correct backend/model customer rate card, make customer settlement independent from provider-cost catalog readiness, and bound all runtime billing-call state to the lifetime of one BillingCallID."
}
48 changes: 24 additions & 24 deletions .kiro/specs/billing-post-usage-correctness-hardening/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@

## Phase 0 — Characterize the Blocking Defects

- [ ] 0.1 Add RED regression for reversed lexical B-leg IDs versus real attempt sequence.
- [x] 0.1 Add RED regression for reversed lexical B-leg IDs versus real attempt sequence.
- Cover failed/canceled/no-surfaced selection and prove current positional reconstruction selects the wrong leg.
- _Boundary: core billing + B2BUA characterization tests_
- _Depends: none_
- _Validation: go test ./internal/core/billing ./internal/core/b2bua_
- _Requirements: 2.1-2.8, 8.1, 8.4_

- [ ] 0.2 Add RED mixed-model customer-pricing regressions.
- [x] 0.2 Add RED mixed-model customer-pricing regressions.
- Prove admission sees route-specific prices while current settlement loses them.
- _Boundary: billingcompose + billing rating tests_
- _Depends: none_
- _Validation: go test ./internal/core/billing ./internal/infra/billingcompose ./internal/infra/billingadmission_
- _Requirements: 4.1-4.8, 8.1, 8.4_

- [ ] 0.3 Add RED customer/operator snapshot independence regression.
- [x] 0.3 Add RED customer/operator snapshot independence regression.
- Missing operator rate must currently reproduce the customer-settlement blockage.
- _Boundary: billingcompose / post-usage worker tests_
- _Depends: none_
- _Validation: go test ./internal/infra/billingcompose ./internal/core/billing_
- _Requirements: 5.1-5.7, 8.2_

- [ ] 0.4 Add retained-state characterization and architecture baseline.
- [x] 0.4 Add retained-state characterization and architecture baseline.
- Exercise many BillingCallIDs through one executor and record current collector growth plus relevant production symbols.
- _Boundary: runtime tests / architecture baseline_
- _Depends: none_
Expand All @@ -40,28 +40,28 @@

## Phase 1 — Preserve B-Leg Sequence Durably

- [ ] 1.1 Add sequence-aware `CallLegUsageRecord` contract and v1/v2 replay tests.
- [x] 1.1 Add sequence-aware `CallLegUsageRecord` contract and v1/v2 replay tests.
- New writes require positive `AttemptSeq`; existing v1 rows remain verifiable.
- _Boundary: core billing contracts_
- _Depends: 0.1_
- _Validation: go test ./internal/core/billing -run 'CallLeg|Sequence|Replay'_
- _Requirements: 2.1-2.8, 3.2-3.6_

- [ ] 1.2 Add Bun sequence migration and cross-dialect store support.
- [x] 1.2 Add Bun sequence migration and cross-dialect store support.
- Nullable legacy column, unique known sequence per call, no guessed backfill.
- _Boundary: billingstore migrations / persistence_
- _Depends: 1.1_
- _Validation: go test ./internal/infra/billingstore -run 'Migration|CallLeg|Sequence'; LIP_REQUIRE_POSTGRES=1 go test -tags=integration ./internal/infra/billingstore -run 'Postgres.*Sequence|PostgresBillingStoreContract'_
- _Requirements: 2.3-2.5, 3.1-3.7, 8.5_

- [ ] 1.3 Thread exact B2BUA sequence through all terminal leg producers.
- [x] 1.3 Thread exact B2BUA sequence through all terminal leg producers.
- Cover opened, never-started, failed-open, swallowed, parallel loser/winner and cancellation paths.
- _Boundary: runtime B2BUA -> billing record seam_
- _Depends: 1.1_
- _Validation: go test ./internal/core/runtime -run 'Billing.*Leg|Parallel|Abort|Failover'_
- _Requirements: 2.1-2.8, 7.1-7.2_

- [ ] 1.4 Make customer leg selection consume only authoritative sequence.
- [x] 1.4 Make customer leg selection consume only authoritative sequence.
- Remove positional reconstruction; legacy unknown sequence fails closed only when order is required.
- _Boundary: core billing rating_
- _Depends: 1.1, 1.3_
Expand All @@ -70,28 +70,28 @@

## Phase 2 — Correct Customer Pricing and Resolver Independence

- [ ] 2.1 Split customer snapshot resolution from operator-rate resolution.
- [x] 2.1 Split customer snapshot resolution from operator-rate resolution.
- Customer path resolves only customer pricing/policy/model cards.
- _Boundary: billingcompose_
- _Depends: 0.3_
- _Validation: go test ./internal/infra/billingcompose_
- _Requirements: 5.1-5.7_

- [ ] 2.2 Carry model-specific pricing through `CallRatingInput`.
- [x] 2.2 Carry model-specific pricing through `CallRatingInput`.
- Remove unused operator-rate collection from customer rating input.
- _Boundary: core billing contracts_
- _Depends: 0.2, 2.1_
- _Validation: go test ./internal/core/billing ./internal/infra/billingcompose_
- _Requirements: 4.1-4.7, 5.2, 5.6_

- [ ] 2.3 Rate selected B-legs with their effective backend/model customer cards.
- [x] 2.3 Rate selected B-legs with their effective backend/model customer cards.
- Default only when no applicable override exists; missing required card fails explicitly.
- _Boundary: core billing rating_
- _Depends: 2.2_
- _Validation: go test ./internal/core/billing -run 'RateCall|Model|Pricing|Failover'_
- _Requirements: 4.1-4.8_

- [ ] 2.4 Prove provider-rate failure cannot hold customer exposure open.
- [x] 2.4 Prove provider-rate failure cannot hold customer exposure open.
- Customer operation settles/closes while provider-cost work stays pending/unreconciled.
- _Boundary: post-usage integration / billingstore_
- _Depends: 2.1, 2.3_
Expand All @@ -100,28 +100,28 @@

## Phase 3 — Replace Executor-Lifetime Billing State

- [ ] 3.1 Introduce private request/BillingCallID-scoped state with RED ownership tests.
- [x] 3.1 Introduce private request/BillingCallID-scoped state with RED ownership tests.
- Allocate once per prepared invocation and define shared retry/parallel/interleaved ownership.
- _Boundary: runtime private lifecycle_
- _Depends: 0.4_
- _Validation: go test ./internal/core/runtime -run 'BillingCallState|BillingCallID|Interleaved|Parallel'_
- _Requirements: 6.1-6.6_

- [ ] 3.2 Move allocated-leg set and terminal timing bounds into call-scoped state.
- [x] 3.2 Move allocated-leg set and terminal timing bounds into call-scoped state.
- Closure reads set/timing only; no financial evidence aggregation.
- _Boundary: runtime terminal billing seam_
- _Depends: 1.3, 3.1_
- _Validation: go test ./internal/core/runtime -run 'Billing.*Closure|Billing.*Leg|Abort'_
- _Requirements: 6.1-6.7, 7.1-7.2_

- [ ] 3.3 Move `FinalizeBilling` single-flight into call-scoped state.
- [x] 3.3 Move `FinalizeBilling` single-flight into call-scoped state.
- External backend finalization occurs outside call-state lock; racing terminal paths share result.
- _Boundary: runtime/backend billing-finalization seam_
- _Depends: 3.1_
- _Validation: go test ./internal/core/runtime -run 'FinalizeBilling|Parallel|Close|Terminal'_
- _Requirements: 6.2-6.6, 6.9, 7.4-7.5_

- [ ] 3.4 Delete executor-global lifetime-growing billing-call maps.
- [x] 3.4 Delete executor-global lifetime-growing billing-call maps.
- Remove obsolete collector eviction helpers and prove no completed call remains reachable from `Executor`.
- _Boundary: runtime simplification_
- _Depends: 3.2, 3.3_
Expand All @@ -130,28 +130,28 @@

## Phase 4 — Brownfield and B2BUA Integration Hardening

- [ ] 4.1 Add legacy sequence-unknown processing tests.
- [x] 4.1 Add legacy sequence-unknown processing tests.
- Completed surfaced and charge-all sequence-independent cases may settle; sequence-dependent ambiguous cases reconcile.
- _Boundary: billing processor/store_
- _Depends: 1.2, 1.4_
- _Validation: go test ./internal/core/billing ./internal/infra/billingstore -run 'Legacy|Sequence|CompleteCall|Reconcile'_
- _Requirements: 3.1-3.7, 8.5_

- [ ] 4.2 Add full failover/parallel/mixed-model B2BUA billing matrix.
- [x] 4.2 Add full failover/parallel/mixed-model B2BUA billing matrix.
- Include opaque reverse-order IDs, different rates, rejected/never-started legs, surfaced winner and cancellations.
- _Boundary: runtime + billing integration_
- _Depends: 1.4, 2.3, 3.4_
- _Validation: go test ./internal/core/runtime ./internal/core/billing ./internal/infra/billingstore_
- _Requirements: 2.8, 4.7-4.8, 7.1-7.5, 8.4-8.5_

- [ ] 4.3 Prove post-output persistence failures still cannot cause provider retry.
- [x] 4.3 Prove post-output persistence failures still cannot cause provider retry.
- Preserve current durable append/outbox semantics while using call-scoped state.
- _Boundary: runtime terminal failure tests_
- _Depends: 3.4_
- _Validation: go test ./internal/core/runtime ./internal/infra/runtimebundle -run 'Billing|Append|Retry|Output'_
- _Requirements: 1.4, 7.3, 7.6_

- [ ] 4.4 Run targeted race and retained-memory tests.
- [x] 4.4 Run targeted race and retained-memory tests.
- Repeated calls on one executor, Recv/Close races, parallel terminalization, finalization single-flight.
- _Boundary: concurrency / runtime lifecycle_
- _Depends: 3.4, 4.2_
Expand All @@ -160,28 +160,28 @@

## Phase 5 — Ratchets and Final Certification

- [ ] 5.1 Add architecture guards for sequence, pricing, resolver and state ownership.
- [x] 5.1 Add architecture guards for sequence, pricing, resolver and state ownership.
- Forbid positional/lexical financial order, customer->operator-rate coupling, and executor-global billing call registries.
- _Boundary: architecture tests_
- _Depends: 1.4, 2.4, 3.4_
- _Validation: go test ./internal/archtest_
- _Requirements: 8.6-8.7_

- [ ] 5.2 Re-run billingstore SQLite/PostgreSQL contract and replay tests.
- [x] 5.2 Re-run billingstore SQLite/PostgreSQL contract and replay tests.
- Include migration from pre-sequence schema plus new writes.
- _Boundary: persistence certification_
- _Depends: 4.1_
- _Validation: go test ./internal/infra/billingstore; LIP_REQUIRE_POSTGRES=1 go test -tags=integration ./internal/infra/billingstore -run 'Postgres.*Billing|Postgres.*Sequence'_
- _Requirements: 3.1-3.7, 8.5, 8.8_

- [ ] 5.3 Update billing architecture/host docs for corrected semantics.
- [x] 5.3 Update billing architecture/host docs for corrected semantics.
- Document attempt sequence as authoritative fact and customer/provider snapshot independence; do not document successor deletions as completed.
- _Boundary: docs / steering_
- _Depends: 5.1_
- _Validation: make docs-check; go test ./internal/archtest_
- _Requirements: 1.1-1.6, 2.1-2.8, 4.1-5.7, 6.1-6.9_

- [ ] 5.4 Perform final spec conformance review and establish successor baseline.
- [x] 5.4 Perform final spec conformance review and establish successor baseline.
- Verify every criterion, no hold regression, no wrong-price/sequence behavior, bounded runtime state, and record exact main SHA/production shape for the convergence spec.
- _Boundary: final certification_
- _Depends: 5.1, 5.2, 5.3_
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Validation and Successor Baseline

Concise Phase 5 certification evidence for `billing-post-usage-correctness-hardening`.
`spec.json` intentionally remains in `tasks-generated` with approvals and `ready_for_implementation: true`; it is not archived because the successor spec owns TUR/LUR cleanup.

## Baseline for `billing-architecture-final-convergence`

- Main branch commit: `bdf6e5037c75a2586015cbf1ecac5207dadc3afe` (parent of this branch).
- Working branch: `feat/billing-post-usage-correctness-hardening` (certification tree at commit `cdbb5d3f`).
- Production shape the successor must preserve:
- one `BillingCallID` per invocation; cheap credit screen -> route/quote -> atomic operational exposure admission -> billing-blind execution -> terminal leg/call records -> post-usage customer settlement (independent of provider-cost readiness).
- positive persisted `attempt_seq` (v2 fingerprint) is the only legal customer-leg ordering source; `ExpectedBLegIDs` is a completeness set.
- customer rating consumes only `CustomerRatingSnapshots` (pricing, policy, per-model cards); `OperatorRate` is provider-cost-only (`ProviderCostJoinResolver.ResolveProviderCost`).
- runtime billing bookkeeping lives in private `billingCallState` on `preparedRequest` / `retryRecvStream`; `Executor` holds no billing-call registry.
- legacy TUR/LUR rating bridge remains a temporary adapter and is a successor deletion target; nothing here deletes it.

## Requirement conformance summary (all green)

| Requirement | Outcome | Evidence |
| :--- | :--- | :--- |
| 1 Monetary authority preserved | PASS | hold-deletion + no-stream-money ratchets active; `go test ./internal/archtest` |
| 2 Actual B-leg attempt order | PASS | sequence contract/rating tests; sequence ratchets; `TestPostgresCallLegSequencePersistence` |
| 3 Pre-fix durable rows safe | PASS | legacy NULL/v1 replay SQLite+Postgres; pre-sequence schema migration test |
| 4 Correct backend/model customer pricing | PASS | model-card resolution through `CallRatingInput`; rating tests |
| 5 Customer/provider independence | PASS | resolver split; operator-freedom ratchets; `provider_cost_independence_test.go` |
| 6 Bounded runtime billing state | PASS | call-scoped state + stress/race tests; state-ownership ratchets |
| 7 Terminal usage/failure semantics | PASS | closure/finalization/parallel/post-output tests |
| 8 Correction proven before cleanup | PASS | Phase 0 RED tests, matrix, ratchets, this review |

## Verification commands run

- `go test ./internal/archtest/...` — pass (incl. new Phase 5.1 ratchets, per-file 500-line limit, docs contract).
- `go test ./internal/core/billing ./internal/core/runtime ./internal/infra/billingcompose ./internal/infra/billingadmission ./internal/infra/runtimebundle ./internal/infra/billingstore` — pass.
- `LIP_REQUIRE_POSTGRES=1 go test -tags=integration ./internal/infra/billingstore` — pass (3 Postgres tests actually ran; skip cleanly when DSN/requirement absent).
- `go test -race ./internal/core/runtime -run 'Billing|Parallel|Close|CallState|Interleaved'` and `go test -race ./internal/infra/billingstore -run 'Concurrent|Sequence|Legacy'` — pass.
- `go vet` affected packages — pass.
- `make quality-checks` — pass. `make test-unit` — pass.
- `make test-race` on Windows is a documented skip (toolchain policy); targeted `-race` runs above provide evidence.

## Residual risks

- Sequences for legacy pre-fix rows remain unknown by design; order-dependent policies on such calls fail closed into reconcile-required (expected brownfield behavior).
- Postgres parity runs relied on a locally configured DSN; CI must keep `LIP_REQUIRE_POSTGRES=1` for the same coverage.
- Full `make test-race` only runs in Linux CI; Windows evidence is from targeted `-race` runs.
2 changes: 1 addition & 1 deletion .kiro/steering/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Core owns orchestration and policy. Core imports `pkg/lipapi` and `pkg/lipsdk`;
- **Streaming**: `stream/` (canonical stream, event pumps), `streamrecovery/`
- **Hooks & Extensions**: `hooks/` (stage evaluation), `extensions/` (stage-four extension platform)
- **Core State & Accounting**: `auxreq/`, `state/`, `traffic/`, `workspace/`, `modelcatalog/`, `modelregistry/`, `accounting/`, `billing/`, `tokenaccounting/`
- `billing/` owns BillingCallID, quote/exposure policy, immutable per-call/per-leg usage contracts, post-usage rating, journal settlement, and billing reports. Runtime performs cheap credit screening and atomic operational exposure admission, then appends terminal usage; it must not enrich prices or write the legacy token ledger.
- `billing/` owns BillingCallID, quote/exposure policy, immutable per-call/per-leg usage contracts (including authoritative persisted `AttemptSeq`), post-usage rating, journal settlement, and billing reports. Runtime performs cheap credit screening and atomic operational exposure admission, then appends terminal usage; it must not enrich prices or write the legacy token ledger. Customer rating resolves customer pricing and model cards only, independent of provider/operator-rate readiness; runtime billing bookkeeping is `BillingCallID`-scoped (no executor-global call registry).
- `tokenaccounting/` remains a protocol/quota usage projection and admin counting surface only; it is not a financial balance or journal input.
- Durable money persistence is `internal/infra/billingstore` (Bun). Host injection is `internal/infra/billingcompose` (snapshot catalog + identity) plus `runtimebundle.ComposeBilling`. Admission adapter is `internal/infra/billingadmission`. Public `pkg/lipruntime.Options` stays non-money.

Expand Down
3 changes: 3 additions & 0 deletions .kiro/steering/tech.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
- **Injection only**: Hosts open the durable store themselves and call `runtimebundle.ComposeBilling`, then pass `ProductionOptions` into `BuildHost`. YAML `accounting.billing.authoritative: true` is a fail-closed gate, not a DSN factory. Stock `lipstd` does not call `ComposeBilling`. Public `pkg/lipruntime.Options` stays non-money.
- **Catalog vs journal**: Snapshot **bodies** live in the process-local catalog (ID+Version). Exposure/call/usage records store immutable refs only. A missing referenced version fails closed at rating.
- **Billing separation**: Operational exposure is not settled money. Admission does not post a journal or mutate balance; customer settlement closes exposure after terminal usage, while provider COGS is an independent per-B-leg operation.
- **Sequence vs order**: the persisted positive `attempt_seq` (v2 fingerprint) is the authoritative B2BUA attempt order; `ExpectedBLegIDs` is a completeness set whose ordering has no financial meaning. Legacy rows keep `attempt_seq NULL` under the v1 contract and fail closed (`ErrBillingAttemptSequenceUnknown`) whenever order-based selection needs a sequence.
- **Snapshot independence**: customer rating resolves only customer pricing/policy/model cards; operator-rate lookup belongs solely to provider-cost resolution, so missing provider-cost data never blocks customer settlement or exposure close.
- **Call-scoped state**: runtime billing bookkeeping lives in one private `billingCallState` per `BillingCallID`/prepared request; the executor owns no lifetime-growing billing-call registry.
- **Leftover YAML**: `accounting.ledger.*` may parse but must not open. Production `accounting.authority` rejects monetary `budget` / `spend_cap` / `money_nano`.

---
Expand Down
Loading