diff --git a/.specs/impact-referrals.md b/.specs/impact-referrals.md index a30c81072b8..df8c37f450f 100644 --- a/.specs/impact-referrals.md +++ b/.specs/impact-referrals.md @@ -123,9 +123,9 @@ BCP 14 [RFC 2119] [RFC 8174] keywords apply only when they appear in all capital after the referral reward was earned. Annual subscription issuances and already-created issuances are not eligible. - **Kilo Pass bonus-like issuance item**: Kilo Pass issuance item of kind `bonus`, `promo_first_month_50pct`, or `referral_bonus`. At most one bonus-like item may exist for an issuance. -- **Referral launch cutoff**: UTC instant configured for the Kilo Pass referral launch. First-time monthly subscribers - who started before the cutoff keep legacy month-2 welcome promo behavior; subscribers starting at or after the cutoff - receive only the first-month welcome promo. +- **Kilo Pass welcome-month issuance**: Monthly base issuance that receives the 50% welcome promo under the + subscription-start schedule in `.specs/kilo-pass.md`. It is streak month 1 for starts before `2026-10-09T00:00:00Z` + and streak month 2 for starts at or after that cutoff. ## Overview @@ -535,8 +535,8 @@ application, and Kilo Pass redeems after local referral bonus allocation. 114. A qualified Kilo Pass referral conversion MUST grant one Kilo Pass referral bonus reward to the referrer unless cap-limited or disqualified. -115. Kilo Pass first-time monthly subscribers MUST keep the first-month welcome promo when otherwise eligible. Referral - rewards MUST NOT replace, reduce, or retroactively alter the source conversion's first-month welcome issuance. +115. Kilo Pass first-time monthly subscribers MUST keep the welcome promo when otherwise eligible. Referral rewards MUST + NOT replace, reduce, or retroactively alter the beneficiary's welcome-month issuance. 116. Kilo Pass referral reward value MUST be snapshotted at conversion as 50% of the referee's eligible monthly tier. Later tier changes by either beneficiary MUST NOT change the reward amount. @@ -551,7 +551,8 @@ application, and Kilo Pass redeems after local referral bonus allocation. rewards represent up to five future monthly referral bonus applications. 120. At eligible Kilo Pass base monthly issuance time, if the beneficiary has one or more pending unexpired Kilo Pass - referral rewards and no permanent application blocker applies, the system MUST consume exactly one reward. + referral rewards, no permanent application blocker applies, and the issuance is not the beneficiary's welcome-month + issuance (Rule 123), the system MUST consume exactly one reward. 121. At most one pending Kilo Pass referral reward MUST be consumed per eligible monthly Kilo Pass base issuance. @@ -563,6 +564,8 @@ application, and Kilo Pass redeems after local referral bonus allocation. 123. A Kilo Pass referral reward MUST apply only to an eligible monthly base issuance after the reward is earned. It MUST NOT apply retroactively to the source conversion's base issuance or to any already-created issuance. + If the issuance is the beneficiary's welcome-month issuance, the reward MUST stay pending for a later issuance. + This applies to referee and referrer rewards. 124. A consumed Kilo Pass referral reward MUST create a distinct `referral_bonus` Kilo Pass issuance item. @@ -587,9 +590,10 @@ application, and Kilo Pass redeems after local referral bonus allocation. rewards but only an annual Kilo Pass subscription, rewards remain pending until an eligible monthly subscription is available or the rewards expire. -132. Kilo Pass welcome bonus behavior MUST change at referral launch: first-time monthly subscribers who started before - the referral launch cutoff keep legacy month-2 promo eligibility; subscribers starting at or after the cutoff MUST - receive only the first-month welcome promo. +132. The monthly welcome schedule MUST follow the subscription-start cutoff in `.specs/kilo-pass.md`. + Eligible first-time subscribers starting before `2026-10-09T00:00:00Z` receive the welcome promo only in month 1. + Those starting at or after that cutoff receive the welcome promo only in month 2. + Returning subscribers MUST always use the normal monthly ramp. ### Shared Reward Granting @@ -813,6 +817,12 @@ application, and Kilo Pass redeems after local referral bonus allocation. ## Changelog +### 2026-10-08 -- Keep the welcome month free of referral rewards + +Defined the welcome-month issuance from the Kilo Pass subscription-start schedule. A pending referral reward now waits +for the next eligible issuance when the current issuance is the beneficiary's welcome month, for both referees and +referrers. This keeps the month-2 welcome promo for subscribers starting at or after `2026-10-09T00:00:00Z`. + ### 2026-08-19 -- Retire the KiloClaw referral program Removed the KiloClaw Advocate referral program. New KiloClaw referral touches, participants, conversions, decisions, and diff --git a/.specs/kilo-pass.md b/.specs/kilo-pass.md index d88275e8084..e63b1665579 100644 --- a/.specs/kilo-pass.md +++ b/.specs/kilo-pass.md @@ -157,10 +157,14 @@ with welcome-promo overrides. Yearly subscriptions use a flat 50% monthly bonus. Bonus-decision paths MUST clamp streak to at least `1` before applying the ramp. 14. Yearly cadence MUST use a flat monthly bonus of 50% of the monthly price and MUST NOT use the monthly ramp or welcome-promo branch. -15. An eligible first-time monthly subscriber MUST receive a 50% bonus in streak month 1 instead of the monthly ramp - value. -16. An eligible first-time monthly subscriber whose subscription start is strictly before `2026-05-07T00:00:00Z` MUST - receive a 50% bonus in streak month 2 instead of the monthly ramp value. +15. For eligible first-time monthly subscribers started strictly before `2026-10-09T00:00:00Z`, streak month 1 MUST use + the 50% welcome bonus, month 2 MUST use the normal 10% ramp, and month 3 MUST use the normal 15% ramp. + This includes subscriptions started before May 7, 2026; no two-month welcome-promo grandfathering remains. +16. For eligible first-time monthly subscribers started at or after `2026-10-09T00:00:00Z`, streak month 1 MUST use the + normal 5% ramp, month 2 MUST use the 50% welcome bonus, and month 3 MUST use the normal 15% ramp. + The subscription start timestamp MUST determine the schedule, with no persisted schedule flag. + Missing or invalid subscription starts MUST conservatively use the legacy month-1 schedule. + Non-first-time subscribers MUST always use the normal monthly ramp, regardless of subscription start. 17. From streak month 3 onward, and in any month where the welcome promo does not apply, monthly cadence MUST use the monthly ramp value. 18. New welcome-promo grants MUST use the `bonus` issuance-item kind with a 50% applied percent. @@ -414,9 +418,8 @@ The following behavior or stronger guarantees are not implemented by current cod 8. Explicit ended-state streak reset for reactivated store subscriptions. 9. Guaranteed expiry for every granted bonus credit. 10. Independent durable audit recording for every failed provider or issuance operation. -11. Retirement of the grandfathered streak-month-2 promo branch after no eligible pre-cutoff subscriptions remain. -12. Store-provider welcome-promo anti-abuse signals equivalent to Stripe fingerprint claims. -13. Atomic prevention of concurrent or repeated active Kilo Pass purchases by the same user. Duplicate-card Rules 51-55 +11. Store-provider welcome-promo anti-abuse signals equivalent to Stripe fingerprint claims. +12. Atomic prevention of concurrent or repeated active Kilo Pass purchases by the same user. Duplicate-card Rules 51-55 intentionally exclude same-user purchases. ## Adjacent Spec Compatibility @@ -431,6 +434,19 @@ promo or Kilo Pass referral conversion. ## Changelog +### 2026-10-08 -- Timestamp-based monthly welcome bonus cutoff + +- Eligible first-time monthly starts strictly before October 9, 2026 at midnight UTC receive 50%, 10%, then 15%. +- Starts at or after the cutoff receive 5%, 50%, then 15%; returning subscribers always use the normal ramp. +- Removed the stored schedule flag and its unshipped migration; subscription start is the only schedule authority. +- Removed the former pre-May-7 two-month welcome bonus, while keeping missing/invalid starts on the month-1 schedule. +- Kept the regular monthly ramp, yearly bonuses, payment eligibility, and usage unlock unchanged. +- Updated subscription offers, ramp previews, and KiloClaw upsells to show the new schedule. +- Kept yearly upsell text at 50% every month and limited welcome callouts to the applicable promo window. +- Kept the selected welcome month in ramp totals after the welcome callout disappears. +- Pending referral rewards skip the welcome-month issuance and apply to the next eligible issuance + (`.specs/impact-referrals.md` Rule 123). + ### 2026-06-08 -- Yearly duplicate-card enforcement - First paid yearly purchases are now subject to the same 24-hour cross-account card-reuse restriction as first paid diff --git a/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx b/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx index 74da8744c99..00ffad9268c 100644 --- a/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx +++ b/apps/web/src/app/(app)/claw/components/billing/PlanSelectionDialog.tsx @@ -11,6 +11,7 @@ import { Badge } from '@/components/ui/badge'; import { KiloPassIcon } from '@/components/icons/KiloPassIcon'; import { cn } from '@/lib/utils'; import { useTRPC } from '@/lib/trpc/utils'; +import { getMonthlyWelcomePromoMonth } from '@kilocode/web-shared/lib/kilo-pass/bonus'; import { createKiloClawSignupDisplay, formatMicrodollars, @@ -92,11 +93,15 @@ function TierCard({ cadence, isSelected, onSelect, + showFirstMonthPromo, + subscriptionStartedAtIso, }: { tier: Tier; cadence: Cadence; isSelected: boolean; onSelect: () => void; + showFirstMonthPromo: boolean; + subscriptionStartedAtIso: string; }) { const data = TIER_DATA[tier]; const isYearly = cadence === 'yearly'; @@ -105,6 +110,7 @@ function TierCard({ const creditsLabel = isYearly ? `$${data.yearlyPrice}/year paid credits` : `$${data.monthlyPrice}/month paid credits`; + const promoMonth = getMonthlyWelcomePromoMonth(subscriptionStartedAtIso); return (