Skip to content

Commit ecd5acc

Browse files
committed
fix(referrals): name Kilo Pass Impact reward unit
1 parent ce9a2f6 commit ecd5acc

3 files changed

Lines changed: 30 additions & 11 deletions

File tree

.specs/impact-referrals.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Updated 2026-05-06 -- require Impact Advocate reward redemption after local Kilo
1818
Updated 2026-05-12 -- note price-versioned KiloClaw billing preserves referral semantics.
1919
Updated 2026-05-22 -- renamed to `.specs/impact-referrals.md` and expanded to Kilo Pass referrals.
2020
Updated 2026-05-28 -- classify enforced Stripe EFW refunds as adverse payments.
21+
Updated 2026-05-29 -- name the Impact-facing Kilo Pass reward unit `Kilo Pass Bonus Credits`.
2122

2223
## Conventions
2324

@@ -631,8 +632,8 @@ application, and Kilo Pass redeems after local referral bonus allocation.
631632
local reward eligibility, application, cancellation, or reversal.
632633

633634
158a. For Kilo Pass, when a local referral bonus reward is allocated/granted, the system MUST queue asynchronous Impact
634-
Advocate reward lookup and single-reward redemption using the reward amount and USD unit so Impact reporting
635-
matches Kilo allocation state.
635+
Advocate reward lookup and single-reward redemption using the USD-denominated reward amount and the
636+
`Kilo Pass Bonus Credits` unit so Impact reporting matches Kilo allocation state.
636637

637638
158b. Kilo Pass Impact Advocate reward redemption MUST be idempotently queued per local reward and MUST NOT block paid
638639
conversion processing, reward ledger creation, reward application, billing settlement, or user access.
@@ -775,6 +776,10 @@ application, and Kilo Pass redeems after local referral bonus allocation.
775776

776777
## Changelog
777778

779+
### 2026-05-29 -- Name the Kilo Pass Impact reward unit
780+
781+
Kilo Pass reward synchronization sends the `Kilo Pass Bonus Credits` unit to Impact Advocate while retaining the USD-denominated local reward amount.
782+
778783
### 2026-05-28 -- Enforced EFW refunds are adverse payments
779784

780785
Classified an enforced Stripe Early Fraud Warning refund as an adverse qualifying payment for both covered products. Pending or earned-but-unapplied rewards cancel, already-applied rewards require support review, and later refund or chargeback delivery must remain idempotent.

apps/web/src/lib/impact/kilo-pass-referrals.test.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@ jest.mock('@/lib/impact/advocate', () => {
1818
sendImpactAdvocateRewardLookupPayload: jest.fn(async () => ({
1919
ok: true,
2020
statusCode: 200,
21-
rewards: [{ id: 'impact-kilo-pass-reward', type: 'CREDIT', amount: 24.5, unit: 'USD' }],
21+
rewards: [
22+
{
23+
id: 'impact-kilo-pass-reward',
24+
type: 'CREDIT',
25+
amount: 24.5,
26+
unit: 'Kilo Pass Bonus Credits',
27+
},
28+
],
2229
responseBody:
23-
'{"rewards":[{"id":"impact-kilo-pass-reward","type":"CREDIT","amount":24.5,"unit":"USD"}]}',
30+
'{"rewards":[{"id":"impact-kilo-pass-reward","type":"CREDIT","amount":24.5,"unit":"Kilo Pass Bonus Credits"}]}',
2431
})),
2532
sendImpactAdvocateRewardRedemptionPayload: jest.fn(async () => ({
2633
ok: true,
@@ -114,9 +121,16 @@ beforeEach(async () => {
114121
mockSendImpactAdvocateRewardLookupPayload.mockResolvedValue({
115122
ok: true,
116123
statusCode: 200,
117-
rewards: [{ id: 'impact-kilo-pass-reward', type: 'CREDIT', amount: 24.5, unit: 'USD' }],
124+
rewards: [
125+
{
126+
id: 'impact-kilo-pass-reward',
127+
type: 'CREDIT',
128+
amount: 24.5,
129+
unit: 'Kilo Pass Bonus Credits',
130+
},
131+
],
118132
responseBody:
119-
'{"rewards":[{"id":"impact-kilo-pass-reward","type":"CREDIT","amount":24.5,"unit":"USD"}]}',
133+
'{"rewards":[{"id":"impact-kilo-pass-reward","type":"CREDIT","amount":24.5,"unit":"Kilo Pass Bonus Credits"}]}',
120134
});
121135
mockSendImpactAdvocateRewardRedemptionPayload.mockResolvedValue({
122136
ok: true,
@@ -383,7 +397,7 @@ describe('Kilo Pass Impact referral conversions', () => {
383397
userId: 'referee@example.com',
384398
rewardTypeFilter: 'CREDIT',
385399
}),
386-
redemption: { amount: 24.5, unit: 'USD' },
400+
redemption: { amount: 24.5, unit: 'Kilo Pass Bonus Credits' },
387401
}),
388402
}),
389403
expect.objectContaining({
@@ -396,7 +410,7 @@ describe('Kilo Pass Impact referral conversions', () => {
396410
userId: 'referrer@example.com',
397411
rewardTypeFilter: 'CREDIT',
398412
}),
399-
redemption: { amount: 24.5, unit: 'USD' },
413+
redemption: { amount: 24.5, unit: 'Kilo Pass Bonus Credits' },
400414
}),
401415
}),
402416
])
@@ -413,7 +427,7 @@ describe('Kilo Pass Impact referral conversions', () => {
413427
{ programKey: ImpactAdvocateProgramKey.KiloPass }
414428
);
415429
expect(mockSendImpactAdvocateRewardRedemptionPayload).toHaveBeenCalledWith(
416-
{ rewardId: 'impact-kilo-pass-reward', amount: 24.5, unit: 'USD' },
430+
{ rewardId: 'impact-kilo-pass-reward', amount: 24.5, unit: 'Kilo Pass Bonus Credits' },
417431
{ programKey: ImpactAdvocateProgramKey.KiloPass }
418432
);
419433

@@ -837,7 +851,7 @@ describe('Kilo Pass Impact referral conversions', () => {
837851
state: 'redeemed',
838852
request_payload: expect.objectContaining({
839853
programKey: ImpactAdvocateProgramKey.KiloPass,
840-
redemption: { amount: 24.5, unit: 'USD' },
854+
redemption: { amount: 24.5, unit: 'Kilo Pass Bonus Credits' },
841855
}),
842856
})
843857
)

apps/web/src/lib/impact/kiloclaw-referrals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const REFERRAL_REWARD_ACTOR = {
129129

130130
const SIGNUP_REFERRAL_TOUCH_CAPTURE_GRACE_MS = 10 * 60 * 1000;
131131
const IMPACT_ADVOCATE_KILOCLAW_REWARD_UNIT = 'MONTH';
132-
const IMPACT_ADVOCATE_KILO_PASS_REWARD_UNIT = 'USD';
132+
const IMPACT_ADVOCATE_KILO_PASS_REWARD_UNIT = 'Kilo Pass Bonus Credits';
133133

134134
function getDatabaseClient(database?: DatabaseClient): DatabaseClient {
135135
return database ?? db;

0 commit comments

Comments
 (0)