| name | subscription-credits-guardrails |
| description | Use when changing Adobe add-on subscription/quota overlay logic, including cancelled-but-not-expired subscriptions, monthly quota, subscribed footer totals, subscription-aware canUseLimit/use behavior, and monthly-first deduction. |
Subscription Credits Guardrails
Use this skill when reviewing or changing Adobe add-on code that touches subscription detection, cancelled-but-not-expired subscription semantics, monthly quota display, subscribed-user permission checks, or subscription-aware deduction.
Related Adobe Skills
Keep this skill as the subscription/quota guardrail layer. Use it together with related Adobe skills when the same project has adjacent repair work:
- Pair with
adobe-add-on-credits-flow whenever subscription logic depends on free credits, paid credits, free_cnt, payment-return refresh, credit cache, balance drift, or successful-use deduction. This skill consumes the normalized credit balance; it does not replace the credits-flow rules.
- Pair with
adobe-add-on-login-loop-fix when subscription refresh, payment return, focus, pageshow, or visibilitychange causes repeated login/status calls.
- Pair with
adobe-add-on-request-params when subscription, status, or credit endpoints also need Adobe public request params or Accept-Language wiring.
- Pair with
migrate-adobe-app-to-template-v2 when subscription/credits are being corrected during a larger template-v2 migration.
- Pair with
adobe-addons-collaboration before editing YiChongmimi repos, and adobe-addons-push when the completed subscription/credits fix needs PR or management sync.
Do not remove the existing subscription optimization points. Add clarifying conditions instead: purchasedCredits means the usable credit balance already normalized by the credits-flow skill, not an unverified sum of raw remote fields.
Specialization Boundary
This skill is the subscription quota overlay. It assumes the generic credit balance has already been normalized by the app/provider, preferably following adobe-add-on-credits-flow.
Own here:
- Detecting subscribed users from reliable server indicators.
- Treating
subscription.detail.cancelled === true as cancellation of renewal only, not loss of current-period benefits before detail.ends_at.
- Computing
monthlyRemaining from monthly_usage_limit and monthly_usage_cnt.
- Composing subscribed usable quota as
monthlyRemaining + normalizedPurchasedCredits.
- Preserving already-known purchased/free credits when a user upgrades to a subscription and the refreshed status omits, nulls, or ambiguously reports the purchased/free balance.
- Ensuring
canUseLimit() and use() agree for subscribed users.
- Deducting monthly quota first, then purchased/free credits when monthly quota is exhausted.
- Regression scenarios for subscribed users, upgrade return, subscribed footer totals, and monthly quota exhaustion.
Do not make this skill the owner of raw credit field parsing, cache fallback, payment-return baselines, or PayLink polling mechanics. Keep those optimization points available by pairing with adobe-add-on-credits-flow when a task crosses into generic credit lifecycle behavior.
What to enforce
- Refresh user status automatically after payment or upgrade return.
- Keep footer quota messaging in sync with the current Canva quota state machine. For subscribed users with monthly quota remaining, footer shows only monthly remaining quota; pay-as-you-go balance is a fallback message after monthly quota is exhausted, or an extra line for unlimited subscriptions.
- After a subscription purchase/upgrade, do not replace a user's existing purchased/free credit balance with the monthly subscription limit. The subscribed usable total must be
monthlyRemaining + existingPurchasedCredits.
- In refresh/merge branches such as
preserveDisplayedCredits, compare and preserve the subscribed usable total, not only the raw free/purchased-credit portion. For example, use min(remoteMonthlyRemaining + remotePurchasedCredits, previousDisplayedTotal), not min(remotePurchasedCredits, previousDisplayedTotal).
- Sync the quota messaging/footer state machine from the current Canva source when present. Do not show subscribed fallback credits as one combined
Use X of Y line after monthly exhaustion. Show the monthly-exhausted title and a separate pay-as-you-go balance line.
- Use the current standard footer copy for monthly-exhausted subscribed users:
- With pay-as-you-go balance:
You have reached your monthly usage limit for the current subscription period. then Now using your pay-as-you-go balance: {balance} {unit} remaining.
- Without pay-as-you-go balance:
You have reached your monthly usage limit for the current subscription period. then Refreshes on {resetDate} or purchase pay-as-you-go.
- The plan line follows after the quota message:
You're on the {appName} {planName} plan. then Manage subscription.
- Treat an active subscription period as higher priority than
subscription.is_subscribed === false.
- Treat
subscription.detail.cancelled === true as still subscribed until detail.ends_at passes.
- Let
canUseLimit() and use() agree on permission and deduction.
- Prevent repeated
canvaid-login calls and unbounded status polling.
Reference
Read references/guardrails.md before changing behavior. It contains the canonical formulas, deduction order, and regression checklist for this skill.
Implementation notes
- Before porting subscription/quota logic from another Adobe add-on, identify the current project's free-credit fallback source: a Provider prop such as
freeTimes, an imported config such as appConfig.freeTimes, or an already-defined local constant. Do not copy fallback symbol names from a reference project unless the same symbol is defined in the target file.
- Treat
DEFAULT_FREE_TIMES, appConfig, getMonthlyRemaining, getPayAsYouGoCredits, and getSubscriptionResetDate as project-shape-sensitive symbols. If any are added during a port, verify they are declared/imported exactly once and wired into the existing context shape without duplicate value keys.
- Prefer status-only refreshes using existing user identity/session data.
- Deduplicate concurrent refreshes and throttle focus/visibility triggers.
- Keep polling short-lived and stop after success or timeout.
- Validate against the regression checklist in the reference file. For TypeScript projects, run
npm run lint:types before npm run build because many Adobe add-ons use webpack/ts-loader with transpile-only behavior.