Skip to content

feat(reconcile): add the BillingPlan reconcile kind - #1854

Merged
rohilsurana merged 5 commits into
feat/billing-plan-apifrom
feat/billing-plan-reconciler
Aug 7, 2026
Merged

feat(reconcile): add the BillingPlan reconcile kind#1854
rohilsurana merged 5 commits into
feat/billing-plan-apifrom
feat/billing-plan-reconciler

Conversation

@rohilsurana

Copy link
Copy Markdown
Member

What

Adds the BillingPlan reconcile kind, so billing plans can be managed declaratively from a desired-state file (the fifth "kind" after Permission, Role, Preference, Webhook, PlatformUser, and BillingProduct).

  • internal/reconcile/billingplan.goBillingPlanSpec, the diff, and validation.
  • internal/reconcile/billingplan_reconciler.go — the reconciler on the AdminService plan APIs (ListAllPlans, CreatePlan, UpdatePlan).
  • cmd/reconcile.go — one registry line; the combined reconcileAPI already satisfies the AdminService subset.

It mirrors the merged BillingProduct kind: Kind/Validate/Reconcile/Export, with fetchCurrent → diff → per-op protovalidateapply, plus out-of-scope handling.

Design

  • Reads use ListAllPlans (empty state = every plan, active and inactive), so Export round-trips inactive plans and the diff sees them.
  • Converged via UpdatePlan: title, description, on_start_credits, trial_days, state.
  • Create-only (immutable after create): name, interval, and the product set. UpdatePlan cannot change them and there is no delete API, so a change to any of them fails the plan (like BillingProduct's immutable prices). A plan is retired by setting its state to inactive, not deleted.
  • Products are referenced by name; the products themselves are managed by the BillingProduct kind. CreatePlan's upsert associates them.
  • Metadata is out of scope: it is not stated in the file, diffed, or exported. But UpdatePlan is a full write of the fields it carries, so the reconciler reads the current metadata and re-sends it on an update to preserve it (real plans carry plan_group_id, etc.).
  • Out of scope: a plan with an empty title or a name shorter than three characters is skipped on fetch, and a file that names it fails the plan.

Deferred (follow-up)

  • Validating that referenced products exist. A clean check needs the server's product list, but during a dry-run the products a BillingProduct doc would create do not exist yet, so it would false-fail cross-kind. Deferred until the framework can validate a plan's product refs against both the server and the file's BillingProduct docs. Today CreatePlan/upsert links products by name; a typo surfaces as a stray product on the next BillingProduct reconcile.
  • Docs (reconcile.mdx) section and reconciler-level tests with a mocked API to follow.

Stacking

Stacked on #1830 (the plan admin APIs it uses). Rebase onto main once #1830 lands.

Testing

Unit tests for the spec validation and the diff (adds, no-op, field updates, state change, immutable interval/products, metadata preservation on update, missing-from-file, duplicates). build, vet, and golangci are green.

@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 7, 2026 5:43am

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1b01a28-0abc-4a95-9f12-35d3eb054689

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@coveralls

coveralls commented Aug 6, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 31151488436

Coverage increased (+0.05%) to 47.99%

Details

  • Coverage increased (+0.05%) from the base build.
  • Patch coverage: 145 uncovered changes across 3 files (171 of 316 lines covered, 54.11%).
  • 1 coverage regression across 1 file.

Uncovered Changes

File Changed Covered %
internal/reconcile/billingplan_reconciler.go 171 45 26.32%
internal/reconcile/billingplan.go 136 118 86.76%
cmd/reconcile.go 9 8 88.89%

Coverage Regressions

1 previously-covered line in 1 file lost coverage.

File Lines Losing Coverage Coverage
cmd/reconcile.go 1 44.44%

Coverage Stats

Coverage Status
Relevant Lines: 39850
Covered Lines: 19124
Line Coverage: 47.99%
Coverage Strength: 15.37 hits per line

💛 - Coveralls

@rohilsurana
rohilsurana force-pushed the feat/billing-plan-reconciler branch from d83141f to 458969a Compare August 7, 2026 05:42
@rohilsurana
rohilsurana merged commit c294c5f into main Aug 7, 2026
8 checks passed
@rohilsurana
rohilsurana deleted the feat/billing-plan-reconciler branch August 7, 2026 06:14
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.

3 participants