fix(billing): migrate default packs to the V4 pricing-card schema - #4463
Conversation
Devkit static-content `packs` still shipped the legacy shape
{ packId, label, priceUsd, meterUnits, featureSections } while
BillingCardComponent (used by BillingPacksComponent) requires the V4
unified schema { id, title, subtitle, price:{amount,period}, cta,
features, ... } — `plans` were migrated in 53ec981, packs were not.
Opening /pricing#units crashed on the unguarded `item.price.amount`
read, and `item.id` being undefined (packs used `packId`) no-opped
the CTA.
- Migrate the 3 default packs to the V4 card schema, preserving the
existing devkit-demo placeholder pricing/quota numbers (id values
unchanged: demo_small/demo_medium/demo_large). Raw Stripe-facing
values (priceUsd, meterUnits) move to `meta`, keyed by the same id
via `meta.packId`, since the extras-checkout modal still needs them.
- billing.subscriptions.component.vue `extrasPacks`: adapt the V4
static-content fallback to the legacy { packId, label, priceUsd,
meterUnits } shape BillingExtrasCheckoutModalComponent expects, so
both the pricing-card render and the extras-checkout modal get their
required shape from the same static-content source. The backend
`packsAvailable` fallback path is untouched (already legacy shape).
- Fix stale docstrings (top-of-file, plans, packs) that told
downstream to replace/override billing.static-content.js directly —
the real contract is `config.billing.staticContent`, resolved by
billing.resolveStaticContent.js (e95d944); this file is
stack-managed and wiped by `/update-stack --theirs`.
- Tests: new suites render/click against the REAL static-content
`packs` export (not a hand-written V4 mock) in both
billing.packs.component.unit.tests.js and
billing.subscriptions.component.unit.tests.js — verified red
(reproduced the exact crash) against the pre-fix shape, green after.
Closes #4458
Claude-Session: https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup
Devkit is public OSS; pack feature copy leaked downstream-product
vocabulary ("scrap runs", "autofix sessions"). Replaced with neutral
demo wording (compute jobs / automation runs) and fixed a misleading
"Saves ~10% vs Small Pack" claim (large is actually ~45% cheaper
per-unit) with neutral "Best per-unit value" phrasing.
Claude-Session: https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup
|
Warning Review limit reached
Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughDefault billing packs now use the V4 pricing-card schema. Subscription extras checkout adapts those packs to its legacy modal contract, with regression tests covering real static content, card rendering, identifiers, and checkout mapping. ChangesBilling pack migration
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4463 +/- ##
=======================================
Coverage 99.56% 99.56%
=======================================
Files 35 35
Lines 1391 1391
Branches 436 436
=======================================
Hits 1385 1385
Misses 6 6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@src/modules/billing/config/billing.static-content.js`:
- Around line 9-10: Update the documentation comment in
billing.static-content.js to clarify that explicit null overrides are honored
only for display-optional keys. Document that billing.resolveStaticContent.js
replaces null plans, packs, and faqs values with the devkit defaults as a
structural-key exception, while preserving the existing precedence description.
- Line 176: Update the expiration text entries in the billing static content,
including the lines corresponding to “Never expires (24mo)” and the two
additional occurrences, so each states one consistent policy: either credits
expire after 24 months or never expire. Ensure no copy combines “never expires”
with a 24-month limit.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 57d47d8c-6001-4531-9e84-d933278c5dfb
📒 Files selected for processing (4)
src/modules/billing/components/billing.subscriptions.component.vuesrc/modules/billing/config/billing.static-content.jssrc/modules/billing/tests/billing.packs.component.unit.tests.jssrc/modules/billing/tests/billing.subscriptions.component.unit.tests.js
…y copy Address CodeRabbit PR #4463 review: - Top-of-file docstring said "project value wins when present, even null" unconditionally, but billing.resolveStaticContent.js only honors explicit null for display-optional keys (pricingMode/tabs/header/halo) — structural keys (plans/packs/faqs) coerce null back to the devkit default since consumers .map()/read properties on them unconditionally. Documented the exception. - "Never expires (24mo)" pack feature line contradicted itself (pre-existing copy, restructured but not reworded by this PR's V4 migration). Dropped "(24mo)" to match the fa-infinity icon's intent.
# [2.3.0](v2.2.0...v2.3.0) (2026-08-01) ### Bug Fixes * **auth,organizations:** guard soft-refresh navigation and the signout race ([#4464](#4464)) ([6a2546e](6a2546e)), closes [#4451](#4451) [#4459](#4459) * **auth,organizations:** soft-refresh (token) at onboarding sites to prevent silent sign-out ([#4431](#4431)) ([5d88766](5d88766)) * **auth,organizations:** surface failures in org-create and email-verification flows ([#4451](#4451)) ([b161df5](b161df5)), closes [#4431](#4431) [#4447](#4447) * **auth:** surface requestJoin failures in the org-setup alert ([#4389](#4389)) ([#4397](#4397)) ([f92003d](f92003d)) * **auth:** token() soft-refresh also carries pendingRequests ([#4436](#4436)) ([0b40e8c](0b40e8c)) * **billing:** config-source the upgrade-prompt pricing copy ([#4465](#4465)) ([9cb370d](9cb370d)), closes [#4460](#4460) * **billing:** meter-mode upgrade prompt copy + single-entry route ([#4410](#4410)) ([df396cf](df396cf)), closes [pricing#units](https://github.com/pricing/issues/units) [pricing#units](https://github.com/pricing/issues/units) * **billing:** migrate default packs to the V4 pricing-card schema ([#4463](#4463)) ([84789b8](84789b8)), closes [pricing#units](https://github.com/pricing/issues/units) [#4458](#4458) * **core,organizations:** config-gate org-required header + unify first-org redirect ([#4423](#4423)) ([7adb298](7adb298)), closes [#4421](#4421) [#4422](#4422) * **core:** wire the computed theme into Vuetify so auto dark-mode works ([#4466](#4466)) ([ed35252](ed35252)), closes [#4462](#4462) * **prerender:** strip local server origin from captured HTML + fail-hard leak assert ([#4502](#4502)) ([#4503](#4503)) ([dbcf8d7](dbcf8d7)) * **skills:** flip PR to ready before waiting on CodeRabbit to avoid a draft deadlock ([#4473](#4473)) ([a733998](a733998)), closes [#4450](#4450) ### Features * **billing:** pricing feature rows wrap + explicit ✗ not-included marker ([#4390](#4390)) ([4bc2515](4bc2515)) * **invitations:** show invite form when user-facing invitations enabled with open signup ([#4501](#4501)) ([daa6688](daa6688)), closes [#4500](#4500) * **skills:** /feature non-interactive invocation — structured SKIP/STOP instead of prompts ([#4455](#4455)) ([197c5f3](197c5f3)), closes [#N](https://github.com/pierreb-devkit/Vue/issues/N) [#failure](https://github.com/pierreb-devkit/Vue/issues/failure) [#4454](#4454) * **ui:** config-overridable loader, plan-badge set, and copy strings ([#4475](#4475)) ([688911a](688911a)), closes [#4474](#4474)
Summary
packsstatic content from the legacy{ packId, label, priceUsd, meterUnits, featureSections }shape to the V4 unified pricing-card schema ({ id, title, subtitle, price:{amount,period}, cta, features, meta:{packId,priceUsd,meterUnits} }) thatBillingCardComponentrequires. Also adaptedbilling.subscriptions.component.vue'sextrasPacksgetter at the consumer boundary so the extras-checkout modal (which still expects the legacy flat shape) maps correctly from the same V4 static-content source, fixed stale docstrings that told downstream projects to edit/override this stack-managed file directly (real contract isconfig.billing.staticContent, resolved bybilling.resolveStaticContent.js), and genericized demo pack copy (scrap runs→compute jobs,autofix sessions→automation runs) plus a misleading "Saves ~10%" claim (large pack is actually ~45% cheaper per-unit) for the public repo.planswere migrated to the V4 schema in 53ec981 butpackswere missed. Opening/pricing#unitscrashed on the unguardeditem.price.amountread (packs had nopriceobject), anditem.idbeingundefined(packs usedpackId) no-opped the CTA.Scope
billingnonelow(static demo content + one adapter getter, no backend/contract changes; Stripe-facingpackId/priceUsd/meterUnitsvalues preserved unchanged undermeta)Validation
npm run lintnpm run test:unitnpm run buildTests: new suites render/click against the REAL
packsexport frombilling.static-content.js(not a hand-written V4 mock) in bothbilling.packs.component.unit.tests.jsandbilling.subscriptions.component.unit.tests.js— verified red (reproduced the exact/pricing#unitscrash) against the pre-fix shape, green after. Full suite: 2474 tests green, build green.Guardrails check
.env*,secrets/**, keys, tokens)Notes for reviewers
billing.static-content.jsis stack-managed (wiped by/update-stack --theirs); downstream projects customize viaconfig.billing.staticContent, unaffected by this change.https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup
Summary by CodeRabbit
New Features
Bug Fixes
Tests