Skip to content

[Budget] commit/release lifecycle와 actual reconciliation 구현 - #66

Closed
Rigu1 wants to merge 12 commits into
tokenpliot:mainfrom
Rigu1:feat/accounting-lifecycle-reconciliation
Closed

[Budget] commit/release lifecycle와 actual reconciliation 구현#66
Rigu1 wants to merge 12 commits into
tokenpliot:mainfrom
Rigu1:feat/accounting-lifecycle-reconciliation

Conversation

@Rigu1

@Rigu1 Rigu1 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

요약

  • 예약 이후 provider 호출과 actual usage 확정까지의 회계 lifecycle을 구현했습니다.
  • active reservation, pending reconciliation liability, committed cost를 bucket 단위로 원자적으로 변경합니다.
  • estimate와 actual token/cost를 연결하고 중복 callback의 이중 정산과 event 중복 발행을 방지합니다.

구현 내용

  • RESERVED, IN_FLIGHT, RECONCILIATION_REQUIRED, COMMITTED, RELEASED, WRITTEN_OFF 상태 전이와 구조화된 transition 결과를 추가했습니다.
  • commit, release, late actual reconciliation, write-off를 ReservationAccounting 단일 진입점으로 제공합니다.
  • actual usage는 예약 시점의 immutable PricingSnapshot으로 한 번만 계산합니다.
  • commit 시 activeReserved -= estimate, committed += actual을 같은 bucket 임계 구역에서 처리합니다.
  • actual 미확정 시 estimate를 pendingReconciliationLiability로 이동하고, late actual 도착 시 pending을 제거한 뒤 actual을 확정합니다.
  • estimate/actual token과 cost, signed delta, over-limit 상태, bounded accounting reason을 정산 결과에 포함했습니다.
  • 정확히 같은 callback은 비용을 재계산하지 않고 REUSED하며, 다른 callback은 비용이 같아도 CONFLICT로 처리합니다.
  • 새롭게 적용된 commit과 late actual에만 framework-independent accounting event를 한 번 전달합니다.
  • listener는 bucket 잠금 밖에서 best-effort at-most-once로 호출합니다.
  • listener 실패는 완료된 정산을 되돌리지 않고 다음 listener의 event 수신도 막지 않습니다.
  • 기존 예약 및 committed-cost API는 호환성을 유지했습니다.

검증

  • ./gradlew :token-pilot-budget:test --rerun-tasks
  • ./gradlew test
  • 전체 30개 Gradle 작업 성공
  • git diff --check

범위 외

체크리스트

필수 테스트

  • estimate보다 작은·같은·큰 actual commit과 delta를 검증한다.
  • actual 초과로 limit을 넘더라도 실제 비용이 누락되지 않고 이후 예약이 BLOCK된다.
  • actual unavailable은 RECONCILIATION_REQUIRED이며 pending liability에 estimate가 한 번 반영된다.
  • late actual이 도착하면 pending liability가 제거되고 COMMITTED로 전이된다.
  • 같은 late actual/commit/release 반복은 회계 상태와 event를 중복 변경하지 않는다.
  • release는 RESERVED 또는 미과금이 확인된 IN_FLIGHT에서만 liability를 제거한다.
  • IN_FLIGHT 만료로 liability가 조용히 사라지지 않는다.
  • 상충하는 terminal 전이는 CONFLICT다.
  • 통화 불일치, 음수 actual과 없는 reservation은 bucket 합계를 변경하지 않는다.
  • request/attempt/reservation과 request/response model correlation이 유지된다.
  • newly COMMITTED인 경우만 cost/accounting event가 한 번 생성된다.
  • listener가 실패해도 상태 전이는 보존되고 duplicate 호출에서 event를 다시 만들지 않는다.

Acceptance Criteria

  • 명시적인 상태 전이표와 허용/거부 transition result가 public contract로 문서화된다.
  • RECONCILIATION_REQUIRED가 late actual로 해결될 수 있다.
  • active reserved, committed와 pending reconciliation liability의 합계 불변식이 모든 전이에서 유지된다.
  • estimate, actual token/cost와 delta가 같은 요청으로 연결된다.
  • 비용은 하나의 ReservationAccounting 경로에서 한 번만 계산·정산된다.
  • 중복 callback에서 비용, 상태와 event가 한 번만 변경된다.
  • 성공, 확인된 미과금 실패와 actual 미확정 실패가 구분된다.
  • 다른 통화가 조용히 합산되지 않는다.
  • accounting event를 #40이 Spring AI 없이 소비할 수 있다.
  • 상태 transition exactly-once와 best-effort event delivery의 한계가 AGENTS.md에 기록된다.
  • 구현이 Accounting lifecycle ADR #46의 전체 계약과 일치한다.

Closes #37

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4abd2050-f799-452e-a45b-869e11f34465


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.

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.

[Budget] commit/release lifecycle와 actual reconciliation 구현

2 participants