Skip to content

feat: release immutable plan versions through reviewed drafts - #1725

Merged
kang-heewon merged 3 commits into
trunkfrom
feat/1539-plan-release-lifecycle
Aug 1, 2026
Merged

feat: release immutable plan versions through reviewed drafts#1725
kang-heewon merged 3 commits into
trunkfrom
feat/1539-plan-release-lifecycle

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Aug 1, 2026

Copy link
Copy Markdown
Member

Outcome

Plan versions now move through optimistic drafts, review, scheduling, publication, supersession, and abandonment while published definitions remain immutable. Review evidence is bound to the exact draft revision and ContractGraph fingerprint, with deterministic structural/provider diagnostics, semantic diffs, impact facts, and estimates.

Publication records intent before registry effects, reconciles ambiguous failures idempotently, prevents overlapping plan-family periods atomically, and emits durable transition events through an idempotent outbox. Open-ended versions derive a successor boundary so an older version cannot become effective again after a finite successor expires.

The publish-profile generated-app canary now models an actually unregistered route guard instead of a self-registering controller, preserving fail-closed DI bootstrap evidence after controllers became automatic providers.

Fixes #1539

Verification

  • pnpm --dir=packages/billing-core test — 152 tests passed.
  • pnpm --filter @croco/billing-core typecheck, lint, and build — passed.
  • pnpm --filter create-croco-app test — 136 tests passed; package typecheck and lint passed.
  • pnpm create-croco-app:smoke production-app-starter — generated tests, typecheck, build, browser journeys, contracts, codegen, and DI graph verification passed.
  • pnpm problem-registry:check — 560 discovered codes matched.
  • pnpm public-api:check and pnpm docs:api:check — passed.
  • pnpm changeset-required:check -- --base origin/trunk --head HEAD — all affected publishable packages covered.
  • pnpm check — 24/25 gates passed, 1 not applicable, 0 failed.
  • pre-push workspace gates — 234/234 test tasks and 233/233 typecheck tasks passed.

Review gates

  • Correctness and regression — PASS. Tests cover lifecycle legality, optimistic revisions, exact evidence binding, semantic diffs, provider preflight, overlap races, publication reconciliation/cancellation, outbox replay ordering, open-ended successor behavior, and the generated DI failure canary.
  • API, security, compatibility, and release — PASS. The public surface is additive, failures use stable Problems, generated contracts and documentation match, and changesets cover billing-core, the generated problems-core registry, and the generated-app fixture correction.
  • Maintainability and minimality — PASS. Lifecycle state, storage constraints, registry publication, and event delivery remain explicit contracts with in-memory reference implementations; the CI compatibility repair changes only the stale negative fixture.
  • Independent adversarial review — PASS after fixes. Publication split-brain recovery, overlap atomicity, durable events, stale evidence, provider bypass, successor boundaries, and ordering findings were corrected and revalidated.

Residual risk

The included store and registry are in-memory reference implementations. Durable production adapters must preserve the documented compare-and-swap, atomic family-overlap, publication-intent, and idempotent outbox contracts.

Summary by CodeRabbit

  • 새 기능
    • 플랜 버전의 초안 작성, 검토, 예약·즉시 게시, 취소 및 대체 lifecycle을 지원합니다.
    • 변경 영향 분석과 검증 결과를 게시 과정에 반영하고, 중복 게시 재시도와 감사 가능한 전환 이벤트를 제공합니다.
    • 좌석 요금, 사용량 구간, 권한, 평가판 및 유효 기간 설정을 지원합니다.
  • 버그 수정
    • 플랜 유효 기간 중복, 잘못된 전환, 오래된 수정본 및 게시 충돌을 명확히 처리합니다.
    • 누락된 제공자 설정을 보호된 경로에서 일관되게 감지합니다.
  • 문서
    • 플랜 릴리스 API, 오류 코드, 복구 지침 및 관련 사용법을 추가·정비했습니다.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kang-heewon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fa237028-8eb2-4ed8-9fe1-32a6f57d9056

📥 Commits

Reviewing files that changed from the base of the PR and between dca0a93 and b5fdf3a.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (92)
  • .changeset/plan-release-lifecycle.md
  • docs/problem-code-registry.json
  • packages/billing-core/README.md
  • packages/billing-core/package.json
  • packages/billing-core/src/index.ts
  • packages/billing-core/src/libs/InMemoryPlanRegistry.ts
  • packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts
  • packages/billing-core/src/libs/PlanRelease.ts
  • packages/billing-core/src/libs/PlanReleaseService.ts
  • packages/billing-core/src/libs/events/PlanReleaseTransitionedEvent.ts
  • packages/billing-core/src/libs/problems/PlanReleaseProblems.ts
  • packages/billing-core/src/tests/InMemoryPlanRegistry.spec.ts
  • packages/billing-core/src/tests/PlanReleaseProblems.spec.ts
  • packages/billing-core/src/tests/PlanReleaseService.spec.ts
  • packages/billing-core/src/types.ts
  • packages/create-croco-app/templates/spa-be-split/apps/api-server/src/tests/app.spec.ts
  • packages/docs/src/content/docs/api/admin-core/src/classes/CreditOperationsValidationProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/DeterministicPlanReleaseImpactAnalyzer.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InMemoryPlanReleaseStore.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseScheduleProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseTransitionProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/OverlappingPlanEffectivePeriodProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseProviderCapabilityProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleasePublishConflictProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseService.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseTransitionedEvent.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseValidationFailedProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/StalePlanReleaseRevisionProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/createPlanVersionSemanticDiff.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/planReleaseCommandFingerprint.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/planVersionDefinitionFingerprint.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseEventPublisher.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseImpactAnalyzer.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseStore.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseValidator.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/CreatePlanDraftCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanEntitlementDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanRelease.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseActor.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseEventDeliveryResult.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactEstimate.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactFact.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactPreview.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseLifecycleEvent.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationFailure.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationIntent.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseReviewEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseServiceDependencies.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseState.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseStoreSaveOptions.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseTransitionCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseTransitionRecord.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseValidationDiagnostic.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseValidationEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanTrialDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanUsageTier.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionSemanticDiffField.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionSemanticDiffRecord.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PublishPlanReleaseCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/SubmitPlanReviewCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/SupersedePlanReleaseCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/UpdatePlanDraftCommand.md
  • packages/docs/src/content/docs/api/events-core/src/classes/DomainEvent.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/BillableUsageJournalRequiredProblem.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/InMemoryBillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/MeterRegistry.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/MeteringService.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/RedisBillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/interfaces/BillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageAppendResult.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageClaimOptions.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageEvent.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageFailure.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageJournalDiagnostics.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageJournalEntry.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDefinition.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredMetadata.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredOptions.md
  • packages/docs/src/content/docs/api/problems-core/src/classes/Problem.md
  • packages/docs/src/content/docs/api/problems-core/src/variables/CROCO_PROBLEM_CODE_REGISTRY.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/classes/DesktopWireSchemaProblem.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireEnumDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireLiteralDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireObjectDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSchemaContext.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSchemaDiagnostic.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSourceLocation.md
  • packages/docs/src/content/docs/api/protocols-rest/src/interfaces/ControllerMetadata.md
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • public-api-surface.snapshot.json
📝 Walkthrough

Walkthrough

billing-core에 draft, in_review, scheduled, published, superseded, abandoned 상태를 가진 Plan Release 수명 주기를 추가했다. PlanRelease 도메인 모델, PlanReleaseService, InMemoryPlanReleaseStore, 관련 Problem 클래스와 전환 이벤트를 신설하고, 공개 API export, 문서, problem registry, changeset을 갱신했다. 별도로 create-croco-app 템플릿의 DI 가드 테스트를 수정하고, 다수 API 문서의 구분선 표기를 정리했다.

Changes

Plan Release 수명 주기 기능

Layer / File(s) Summary
플랜 도메인 타입 및 registry 검증
packages/billing-core/src/types.ts, packages/billing-core/src/libs/InMemoryPlanRegistry.ts, packages/billing-core/src/tests/InMemoryPlanRegistry.spec.ts
PlanVersionDefinition에 좌석 단가, 사용량 등급, entitlement, trial, 종료 시각 필드를 추가했다. getPlanAtDate가 종료 시각과 다음 버전 시작 시각을 고려하도록 변경되고, 신규 필드에 대한 검증과 동결 로직이 추가되었다.
PlanRelease 도메인 모델, 문제, 이벤트
packages/billing-core/src/libs/PlanRelease.ts, packages/billing-core/src/libs/problems/PlanReleaseProblems.ts, packages/billing-core/src/libs/events/PlanReleaseTransitionedEvent.ts, packages/billing-core/src/tests/BillingProblems.spec.ts
Plan Release 상태·명령·증거 타입, semantic diff 및 fingerprint 함수, 7개 신규 Problem 클래스, PlanReleaseTransitionedEvent를 추가했다.
InMemoryPlanReleaseStore
packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts
릴리스 생성·조회·목록화·저장, 유효 기간 중복 검사, 대기 이벤트 관리를 제공하는 저장소 구현을 추가했다.
PlanReleaseService 구현 및 테스트
packages/billing-core/src/libs/PlanReleaseService.ts, packages/billing-core/src/tests/PlanReleaseService.spec.ts
초안 생성·수정, 검토 제출, 예약·즉시 게시, 포기·게시 취소·대체 전환과 DeterministicPlanReleaseImpactAnalyzer를 구현했다. 테스트가 전이, 동시성, 이벤트 outbox 재전달을 검증한다.
공개 export 및 패키지 배선
packages/billing-core/src/index.ts, packages/billing-core/package.json, public-api-surface.snapshot.json, .changeset/plan-release-lifecycle.md
신규 타입·클래스·문제를 공개 export하고, @croco/protocols-core 의존성과 public API 스냅샷, 릴리스 계획 changeset을 추가했다.
Plan Release API 문서 및 problem registry
packages/billing-core/README.md, packages/docs/src/content/docs/api/billing-core/..., docs/problem-code-registry.json, packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
README와 billing-core API 문서에 Plan Release 계약을 문서화하고, 7개 신규 문제 코드를 problem registry와 복구 cookbook에 등록했다.

Estimated code review effort: 4 (Complex) | ~75 minutes

create-croco-app DI 가드 테스트 수정

Layer / File(s) Summary
MissingProviderGuard 적용 및 테스트 갱신
packages/create-croco-app/templates/spa-be-split/apps/api-server/src/tests/app.spec.ts
MissingProviderGuard를 GET 라우트에 연결하고, DI 부트스트랩 테스트가 누락된 provider를 가드 위치에서 검증하도록 변경했다.

Estimated code review effort: 2 (Simple) | ~10 minutes

문서 마크다운 구분선 표기 정리

Layer / File(s) Summary
문서 구분선 및 주석 서식 정리
packages/docs/src/content/docs/api/admin-core/..., packages/docs/src/content/docs/api/metering-core/..., packages/docs/src/content/docs/api/protocols-desktop/..., packages/docs/src/content/docs/api/protocols-rest/..., packages/docs/src/content/docs/api/problems-core/src/classes/Problem.md
여러 패키지 API 문서에서 마크다운 구분선을 ---에서 ***로 변경하고, 예제 주석 공백을 정리했다.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

Possibly related PRs

  • croco-dev/framework#1027: billing/entitlement 계약 검증과 ContractGraph가 소비하는 메타데이터를 함께 변경한다.
  • croco-dev/framework#1551: 낙관적 동시성, idempotency, 검증 증거, 감사 가능한 이벤트/이력을 가진 불변 버전 수명 주기 전이를 추가한다는 점에서 유사하다.
  • croco-dev/framework#1726: PlanReleaseProviderCapabilityProblemPlanReleaseService의 provider capability 사전 점검이 소비하는 conformance 증거를 추가한다.
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 주요 수명 주기 기능은 구현되었지만 unknown plan version 및 already-published mutation 문제 요구사항의 구현 근거가 없습니다. 두 요구사항에 대한 안정적 Problem 클래스, 레지스트리, 문서, 테스트를 추가하고 서비스에서 해당 오류를 발생시키십시오.
Out of Scope Changes check ⚠️ Warning metering-core, protocols-desktop, admin-core의 무관한 문서 서식 변경과 create-croco-app DI 변경이 포함되었습니다. 무관한 문서 및 create-croco-app 변경을 별도 PR로 이동하거나 #1539와의 명시적 근거를 추가하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 검토된 초안을 통한 불변 플랜 버전 릴리스라는 PR의 핵심 변경을 명확하고 간결하게 설명합니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1539-plan-release-lifecycle

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.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

📊 Benchmark Results

✅ All benchmarks passed

Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 43.6μs 30.0ms 8.2μs +433.5% -
CrocoApp lambdaHandler (10 controllers) 2.4ms 50.0ms 258.4μs +822.2% -
Lambda cold-start simulation 1.7ms 80.0ms 418.1μs +313.6% -
Lambda cold-start with headers 1.5ms 80.0ms 369.7μs +311.2% -
Lambda cold-start with binary body 1.5ms 80.0ms 339.1μs +334.9% -
Lambda cold-start with query params 1.5ms 80.0ms 301.3μs +388.4% -
Lambda cold-start with authorizer context 1.5ms 80.0ms 299.8μs +393.4% -
Lambda cold-start realistic scenario 1.4ms 80.0ms 299.2μs +383.4% -
EventBusConfig.start (10 handlers) 1.7μs 10.0ms 1.4μs +18.1% -
EventPublisher.publishNow single event 1.9μs 2.0ms 1.7μs +13.1% -
DefaultHandlerResolver.resolve × 10 0.1μs 5.0ms 0.1μs +0.0% -
Container.get singleton (cold) 92.5μs 5.0ms 70.3μs +31.6% -
Container.register × 50 components 3.4ms 10.0ms 3.2ms +4.4% -
Container.validate (50 components) 3.8ms 20.0ms 3.4ms +13.3% -
Container.get singleton (warm) 1.7μs 500.0μs 1.6μs +1.2% -
TelemetryRuntime.init (lambda preset) 2.2μs 200.0ms 1.1ms -99.8% -
lambdaPreset config creation 1.5μs 2.0ms 1.4μs +3.6% -

Updated: 2026-08-01T21:19:41.360Z · Commit: 5a54f42

@kang-heewon
kang-heewon force-pushed the feat/1539-plan-release-lifecycle branch from a1ae54d to dca0a93 Compare August 1, 2026 20:24

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 24

🤖 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 `@packages/billing-core/src/index.ts`:
- Around line 127-167: Reorder the Plan Release exports in index.ts so all value
exports, including PlanReleaseTransitionedEvent and the
PlanRelease/PlanReleaseService symbols, appear before the type-only export
block. Keep every existing export unchanged and place the complete Plan Release
type export last within this category.

In `@packages/billing-core/src/libs/events/PlanReleaseTransitionedEvent.ts`:
- Around line 9-19: Update the PlanReleaseTransitionedEvent constructor to
accept one typed object containing planVersionRef, from, to, revision, actorId,
reason, and optional eventId instead of positional arguments. Update
PlanReleaseService.createEvent and every constructor call to pass these values
by property name, preserving existing behavior and eliminating actorId/reason
ordering errors.

In `@packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts`:
- Around line 93-108: 중복된 effectivePeriodsConflict 구현을 공유 함수로 통합하십시오.
packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts의 93-108행에서는 지역 함수를
제거하고 ./PlanRelease의 export 함수를 import하며 매개변수 타입을 PlanVersionDefinition으로 맞추십시오.
packages/billing-core/src/libs/PlanReleaseService.ts의 656-671행에서도 지역 함수를 제거하고
동일한 함수를 import하십시오. 구현 본문은 packages/billing-core/src/libs/PlanRelease.ts로 이동해
export하여 사전 검사와 최종 검사가 항상 같은 판정 로직을 사용하게 하십시오.

In `@packages/billing-core/src/libs/PlanRelease.ts`:
- Around line 314-326: Update stableStringify’s object serialization to omit
keys whose values are undefined before sorting and joining entries, so objects
with explicitly undefined optional fields produce the same fingerprint as
objects without those fields. Preserve serialization of all defined values and
array elements.
- Around line 282-288: Update the usage_tiers comparator in PlanRelease so tiers
sharing the same meterKey and upTo rank are deterministically ordered by
unitAmount as the secondary key. Preserve the existing meterKey and upTo
ordering, including treating null upTo as the highest rank, and ensure this
tie-break applies when upTo values are both null.
- Around line 252-259: Update planReleaseCommandFingerprint to return a sha256:
digest of the stableStringify payload, matching
planVersionDefinitionFingerprint, so actor and reason are not persisted in
plaintext or unbounded form. Locate every idempotency comparison in
PlanReleaseService that compares stored
PlanReleasePublicationEvidence.commandFingerprint or
PlanReleasePublicationFailure.commandFingerprint and make it use the same digest
format, including any required handling for existing stored records.

In `@packages/billing-core/src/libs/PlanReleaseService.ts`:
- Around line 357-375: Update cancelPublish’s cancellation path to create the
cancelled record through the existing transition/saveTransition flow instead of
directly calling store.save. Preserve the current state changes, add the
corresponding history entry and PlanReleaseLifecycleEvent to the save options,
and invoke deliverPendingEvents after persistence so the revision remains
traceable and the cancellation is audited.
- Around line 105-116: Update the validation failure branch in
PlanReleaseService to collect a distinct diagnostic code for each failed
evidence check: planVersionRef, definitionFingerprint, draftRevision, empty
graphVersion, empty snapshotId, and invalid checkedAt. Pass the collected codes
in deterministic sorted order to PlanReleaseValidationFailedProblem instead of
using the single validation-snapshot-definition-mismatch code.
- Around line 198-200: Update the empty idempotencyKey validation in the command
handling flow to throw the established validation-related Problem instead of
PlanReleasePublishConflictProblem; keep PlanReleasePublishConflictProblem
reserved for actual competing publish requests.
- Around line 537-548: Update the pending-event loop in the release publication
flow to stop publishing further events for a planVersionRef after one event
fails, while continuing to process later events belonging to other releases.
Preserve the failed event as a failure and leave skipped events unmarked so they
remain pending; ensure the pending aggregate only reflects attempted publication
failures, not skipped events.
- Around line 487-514: Update publishNow and
recordDeterministicPublicationFailure so compensation-save failures are absorbed
and the original InvalidPlanVersionDefinitionProblem or
PlanVersionConflictProblem remains the propagated error. Ensure the helper
records the failure without creating history entries or outbox events, and align
its cleanup behavior with cancelPublish. Model any recovery outcome using the
existing stable diagnostic mechanism rather than a generic catch-all or silent
fallback.

In `@packages/billing-core/src/libs/problems/PlanReleaseProblems.ts`:
- Around line 3-13: Update StalePlanReleaseRevisionProblem to expose expected
and actual through the Problem extensions, following the existing extensions
pattern used by PlanReleaseValidationFailedProblem and
PlanReleaseProviderCapabilityProblem while preserving the current diagnostic
code and message.
- Around line 15-25: Update the constructor signature of
InvalidPlanReleaseTransitionProblem so from uses PlanReleaseState | null and to
uses PlanReleaseState, reusing the existing PlanReleaseState union from the
package while leaving the error message behavior unchanged.

In `@packages/billing-core/src/tests/BillingProblems.spec.ts`:
- Around line 8-14: Update the PlanReleaseProblems tests by moving them from
BillingProblems.spec.ts to src/tests/PlanReleaseProblems.spec.ts, and extend
coverage to include InvalidPlanReleaseScheduleProblem and
PlanReleasePublishConflictProblem. Assert the stable problem-code strings for
all seven classes defined in PlanReleaseProblems.ts, preserving the existing
assertions for the five covered classes.

In `@packages/billing-core/src/tests/InMemoryPlanRegistry.spec.ts`:
- Around line 366-385: Update both asynchronous rejection assertions in the
InMemoryPlanRegistry tests to use rejects.toThrow with
InvalidPlanVersionDefinitionProblem, preserving the existing class-matching
validation for duplicate usage tiers and entitlements.

In `@packages/billing-core/src/tests/PlanReleaseService.spec.ts`:
- Around line 863-888: Extend the test “keeps failed lifecycle events in the
outbox and redelivers them idempotently” to call service.deliverPendingEvents
while shouldFail remains true and assert attempted: 1, published: 0, pending: 1,
with failures containing the event ID and detail “publisher unavailable”; then
retain the existing retry assertions after setting shouldFail to false.
- Around line 431-465: Update the rejection assertions in
PlanReleaseService.spec.ts for both structural.service.submitReview and
provider.service.submitReview to use rejects.toMatchObject, preserving the
expected Problem classes while also asserting the deterministic diagnostic code
lists: the structural case must include its validation diagnostic code, and the
provider case must include "polar/meter-missing".
- Around line 190-281: Split the combined “supports the explicit review, return,
schedule, abandon, publish, and supersede transitions” test into separate it
blocks, each covering one transition and retaining its existing assertions. Add
cases verifying every attempted transition from an abandoned release rejects
with InvalidPlanReleaseTransitionProblem, that abandon on a published release
has the expected result, and that updateDraft rejects with
PlanReleasePublishConflictProblem while publicationIntent is active; use the
existing abandon tests as context without duplicating unrelated setup.

In
`@packages/docs/src/content/docs/api/billing-core/src/classes/InMemoryPlanReleaseStore.md`:
- Line 28: Update the original TypeScript JSDoc for InMemoryPlanReleaseStore to
describe pendingEvents as in-memory and non-durable, removing the durable outbox
claim. Regenerate the API Markdown with pnpm docs:api:check, and do not edit the
generated Markdown directly.

In
`@packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseScheduleProblem.md`:
- Line 8: packages/billing-core/src/libs/problems/PlanReleaseProblems.ts의
InvalidPlanReleaseScheduleProblem, InvalidPlanReleaseTransitionProblem,
OverlappingPlanEffectivePeriodProblem source JSDoc을 각 concrete Problem의 실제 역할에
맞게 수정하세요. 생성 문서는 직접 편집하지 말고 pnpm docs:api:check를 실행해 다음 문서를 갱신하세요:
packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseScheduleProblem.md
(8-8),
packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseTransitionProblem.md
(8-8),
packages/docs/src/content/docs/api/billing-core/src/classes/OverlappingPlanEffectivePeriodProblem.md
(8-8).

In
`@packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseProviderCapabilityProblem.md`:
- Line 8: 공개 타입별 역할과 맞지 않는 공통 JSDoc 설명을 원본 TypeScript 선언에서 수정하세요.
PlanReleaseProviderCapabilityProblem은 provider capability preflight 실패 Problem,
PlanReleasePublishConflictProblem은 publication idempotency conflict Problem,
PlanReleaseTransitionedEvent는 구체적인 DomainEvent,
PlanReleaseValidationFailedProblem은 validation 실패 Problem으로 설명하고, 네 생성 문서 파일은 직접
수정하지 마세요. 변경 후
packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseProviderCapabilityProblem.md:8,
packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleasePublishConflictProblem.md:8,
packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseTransitionedEvent.md:8,
packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseValidationFailedProblem.md:8에
반영되도록 TypeDoc을 재생성하고 pnpm docs:api:check 및 문서 빌드로 검증하세요.

In
`@packages/docs/src/content/docs/api/billing-core/src/classes/StalePlanReleaseRevisionProblem.md`:
- Line 8: StalePlanReleaseRevisionProblem과 PlanEntitlementDefinition의 원본
TypeScript JSDoc을 API 정의에 맞게 갱신하세요. StalePlanReleaseRevisionProblem은
billing/stale-plan-release-revision revision 충돌 Problem으로,
PlanEntitlementDefinition은 plan id/version 참조와 entitlement discriminated union
체계를 설명하도록 수정하고, 생성 문서
packages/docs/src/content/docs/api/billing-core/src/classes/StalePlanReleaseRevisionProblem.md:8
및
packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanEntitlementDefinition.md:10은
재생성으로 반영하세요. 이후 pnpm docs:api:check를 실행해 생성 문서 drift가 없는지 확인하세요.

In
`@packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseValidationEvidence.md`:
- Line 10: The PlanReleaseService.submitReview documentation must distinguish
skipping ContractGraph re-validation from verifying snapshot binding. Update the
original TypeScript JSDoc for the relevant PlanRelease validation evidence type
to state that validation is not re-run but planVersionRef,
definitionFingerprint, draftRevision, graphVersion, snapshotId, and checkedAt
bindings are checked; regenerate the TypeDoc output, then validate with pnpm
docs:api:check and the documentation build.

In
`@packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanTrialDefinition.md`:
- Line 10: PlanTrialDefinition과 PlanUsageTier의 공통 generic 설명을 각 타입의 실제 계약에 맞는 원본
TypeScript JSDoc으로 교체하세요. PlanTrialDefinition에는 trial 기간과 결제 수단 요구 여부를,
PlanUsageTier에는 meter key·단위 금액·사용량 상한을 설명하고 TypeDoc을 재생성하세요. 생성물인
packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanTrialDefinition.md:10과
packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanUsageTier.md:10은
직접 수정하지 말고, 변경 후 pnpm docs:api:check와 문서 빌드로 검증하세요.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f0efb578-4677-4099-8b97-f3becf25f55e

📥 Commits

Reviewing files that changed from the base of the PR and between f5f0b7d and dca0a93.

⛔ Files ignored due to path filters (2)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (92)
  • .changeset/plan-release-lifecycle.md
  • docs/problem-code-registry.json
  • packages/billing-core/README.md
  • packages/billing-core/package.json
  • packages/billing-core/src/index.ts
  • packages/billing-core/src/libs/InMemoryPlanRegistry.ts
  • packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts
  • packages/billing-core/src/libs/PlanRelease.ts
  • packages/billing-core/src/libs/PlanReleaseService.ts
  • packages/billing-core/src/libs/events/PlanReleaseTransitionedEvent.ts
  • packages/billing-core/src/libs/problems/PlanReleaseProblems.ts
  • packages/billing-core/src/tests/BillingProblems.spec.ts
  • packages/billing-core/src/tests/InMemoryPlanRegistry.spec.ts
  • packages/billing-core/src/tests/PlanReleaseService.spec.ts
  • packages/billing-core/src/types.ts
  • packages/create-croco-app/templates/spa-be-split/apps/api-server/src/tests/app.spec.ts
  • packages/docs/src/content/docs/api/admin-core/src/classes/CreditOperationsValidationProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/DeterministicPlanReleaseImpactAnalyzer.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InMemoryPlanReleaseStore.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseScheduleProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/InvalidPlanReleaseTransitionProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/OverlappingPlanEffectivePeriodProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseProviderCapabilityProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleasePublishConflictProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseService.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseTransitionedEvent.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/PlanReleaseValidationFailedProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/classes/StalePlanReleaseRevisionProblem.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/createPlanVersionSemanticDiff.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/planReleaseCommandFingerprint.md
  • packages/docs/src/content/docs/api/billing-core/src/functions/planVersionDefinitionFingerprint.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseEventPublisher.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseImpactAnalyzer.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseStore.md
  • packages/docs/src/content/docs/api/billing-core/src/interfaces/PlanReleaseValidator.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/CreatePlanDraftCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanEntitlementDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanRelease.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseActor.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseEventDeliveryResult.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactEstimate.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactFact.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseImpactPreview.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseLifecycleEvent.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationFailure.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleasePublicationIntent.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseReviewEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseServiceDependencies.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseState.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseStoreSaveOptions.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseTransitionCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseTransitionRecord.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseValidationDiagnostic.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanReleaseValidationEvidence.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanTrialDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanUsageTier.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionDefinition.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionSemanticDiffField.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PlanVersionSemanticDiffRecord.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/PublishPlanReleaseCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/SubmitPlanReviewCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/SupersedePlanReleaseCommand.md
  • packages/docs/src/content/docs/api/billing-core/src/type-aliases/UpdatePlanDraftCommand.md
  • packages/docs/src/content/docs/api/events-core/src/classes/DomainEvent.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/BillableUsageJournalRequiredProblem.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/InMemoryBillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/MeterRegistry.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/MeteringService.md
  • packages/docs/src/content/docs/api/metering-core/src/classes/RedisBillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/interfaces/BillableUsageJournal.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageAppendResult.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageClaimOptions.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageEvent.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageFailure.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageJournalDiagnostics.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/BillableUsageJournalEntry.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeterDefinition.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredMetadata.md
  • packages/docs/src/content/docs/api/metering-core/src/type-aliases/MeteredOptions.md
  • packages/docs/src/content/docs/api/problems-core/src/classes/Problem.md
  • packages/docs/src/content/docs/api/problems-core/src/variables/CROCO_PROBLEM_CODE_REGISTRY.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/classes/DesktopWireSchemaProblem.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireEnumDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireLiteralDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireObjectDescriptor.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSchemaContext.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSchemaDiagnostic.md
  • packages/docs/src/content/docs/api/protocols-desktop/src/type-aliases/DesktopWireSourceLocation.md
  • packages/docs/src/content/docs/api/protocols-rest/src/interfaces/ControllerMetadata.md
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • public-api-surface.snapshot.json

Comment thread packages/billing-core/src/index.ts Outdated
Comment thread packages/billing-core/src/libs/events/PlanReleaseTransitionedEvent.ts Outdated
Comment thread packages/billing-core/src/libs/InMemoryPlanReleaseStore.ts Outdated
Comment thread packages/billing-core/src/libs/PlanRelease.ts
Comment thread packages/billing-core/src/libs/PlanRelease.ts Outdated
@kang-heewon
kang-heewon force-pushed the feat/1539-plan-release-lifecycle branch from dca0a93 to b5fdf3a Compare August 1, 2026 21:10
@kang-heewon
kang-heewon merged commit 986ce2d into trunk Aug 1, 2026
12 checks passed
@kang-heewon
kang-heewon deleted the feat/1539-plan-release-lifecycle branch August 1, 2026 21:53
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.

[billing-core] Add draft, review, and publish transitions for immutable plan versions

1 participant