From 9b65090f10d6010ca4c28f8be2d9b6bf42825fe5 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:47:35 +0000 Subject: [PATCH 1/2] Change free trial duration from 30 days to 14 days - Update TRIAL_DURATION_DAYS constant from 30 to 14 - Update fallback comment in trial-utils to reference constant name - Update all user-facing copy (create org page, no-orgs state, Slack workspace selector, teams get-started page, notifications) - Update backfill script SQL INTERVAL from 30 to 14 days - Update test assertions to reflect 14-day trial duration --- .../slack/_components/WorkspaceSelector.tsx | 2 +- src/app/get-started/teams/page.tsx | 2 +- .../organizations/NoOrganizationsState.tsx | 2 +- .../new/CreateOrganizationPage.tsx | 4 +-- src/lib/constants.ts | 2 +- src/lib/notifications.ts | 2 +- src/lib/organizations/trial-utils.test.ts | 26 +++++++++---------- src/lib/organizations/trial-utils.ts | 2 +- src/scripts/db/backfill-free-trial-end-at.ts | 4 +-- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/app/get-started/slack/_components/WorkspaceSelector.tsx b/src/app/get-started/slack/_components/WorkspaceSelector.tsx index 757a241cd..d01af8cc3 100644 --- a/src/app/get-started/slack/_components/WorkspaceSelector.tsx +++ b/src/app/get-started/slack/_components/WorkspaceSelector.tsx @@ -175,7 +175,7 @@ export function WorkspaceSelector({ onSelect }: WorkspaceSelectorProps) { Create New Organization - Share Kilo for Slack with your team. Starts with a 30-day free trial + Share Kilo for Slack with your team. Starts with a 14-day free trial diff --git a/src/app/get-started/teams/page.tsx b/src/app/get-started/teams/page.tsx index de10ad90e..80c6af173 100644 --- a/src/app/get-started/teams/page.tsx +++ b/src/app/get-started/teams/page.tsx @@ -15,7 +15,7 @@ export default async function TeamsGetStartedPage({ searchParams }: GetStartedPa callbackPath="/organizations/new" searchParams={params} error={error} - signUpText="Try out Kilo Teams with a 30-day free trial, no credit card required. After you sign up, you can directly onboard all your team members." + signUpText="Try out Kilo Teams with a 14-day free trial, no credit card required. After you sign up, you can directly onboard all your team members." /> ); diff --git a/src/components/organizations/NoOrganizationsState.tsx b/src/components/organizations/NoOrganizationsState.tsx index 838be2ffb..8e9fe07f1 100644 --- a/src/components/organizations/NoOrganizationsState.tsx +++ b/src/components/organizations/NoOrganizationsState.tsx @@ -80,7 +80,7 @@ export function NoOrganizationsState() { {canMakeOrgs ? (

- Take Kilo on a free 30-day test drive for your team + Take Kilo on a free 14-day test drive for your team

diff --git a/src/components/organizations/new/CreateOrganizationPage.tsx b/src/components/organizations/new/CreateOrganizationPage.tsx index 5858402c6..fdb7050e1 100644 --- a/src/components/organizations/new/CreateOrganizationPage.tsx +++ b/src/components/organizations/new/CreateOrganizationPage.tsx @@ -144,7 +144,7 @@ export function CreateOrganizationPage({ mockSelectedOrgName }: CreateOrganizati

Create an organization and start your
- 30-day free trial for Kilo Enterprise + 14-day free trial for Kilo Enterprise

- Start 30-day free trial for Kilo Enterprise + Start 14-day free trial for Kilo Enterprise )} diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 73aa9c502..6f61dfc98 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -28,7 +28,7 @@ export const APP_URL = ? 'https://app.kilo.ai' : (process.env.APP_URL_OVERRIDE ?? 'http://localhost:3000'); -export const TRIAL_DURATION_DAYS = 30; +export const TRIAL_DURATION_DAYS = 14; export const AUTOCOMPLETE_MODEL = 'codestral-2508'; diff --git a/src/lib/notifications.ts b/src/lib/notifications.ts index 85c0007cb..914d442b8 100644 --- a/src/lib/notifications.ts +++ b/src/lib/notifications.ts @@ -142,7 +142,7 @@ async function generateTeamsTrialNotification(user: User): Promise { it('returns trial_active for 8+ days remaining', () => { - expect(getOrgTrialStatusFromDays(30)).toBe('trial_active'); + expect(getOrgTrialStatusFromDays(14)).toBe('trial_active'); expect(getOrgTrialStatusFromDays(8)).toBe('trial_active'); }); @@ -54,29 +54,29 @@ describe('getDaysRemainingInTrial', () => { // Organization created 5 days before fixed now const createdAt = new Date(FIXED_NOW_MS - 5 * 24 * 60 * 60 * 1000).toISOString(); - // Organization with trial ending in 30 days - const freeTrialEndAt30 = new Date(FIXED_NOW_MS + 30 * 24 * 60 * 60 * 1000).toISOString(); - expect(getDaysRemainingInTrial(freeTrialEndAt30, createdAt)).toBe(30); + // Organization with trial ending in 14 days + const freeTrialEndAt14 = new Date(FIXED_NOW_MS + 14 * 24 * 60 * 60 * 1000).toISOString(); + expect(getDaysRemainingInTrial(freeTrialEndAt14, createdAt)).toBe(14); // Organization with trial expired 5 days ago const freeTrialEndAtExpired = new Date(FIXED_NOW_MS - 5 * 24 * 60 * 60 * 1000).toISOString(); expect(getDaysRemainingInTrial(freeTrialEndAtExpired, createdAt)).toBe(-5); }); - it('falls back to created_at + 30 days when free_trial_end_at is null', () => { + it('falls back to created_at + 14 days when free_trial_end_at is null', () => { // Organization created today (no free_trial_end_at set) - expect(getDaysRemainingInTrial(null, FIXED_NOW)).toBe(30); + expect(getDaysRemainingInTrial(null, FIXED_NOW)).toBe(14); // Organization created 10 days ago (no free_trial_end_at set) const tenDaysAgo = new Date(FIXED_NOW_MS - 10 * 24 * 60 * 60 * 1000).toISOString(); - expect(getDaysRemainingInTrial(null, tenDaysAgo)).toBe(20); + expect(getDaysRemainingInTrial(null, tenDaysAgo)).toBe(4); - // Organization created 30 days ago (expires today, no free_trial_end_at set) - const thirtyDaysAgo = new Date(FIXED_NOW_MS - 30 * 24 * 60 * 60 * 1000).toISOString(); - expect(getDaysRemainingInTrial(null, thirtyDaysAgo)).toBe(0); + // Organization created 14 days ago (expires today, no free_trial_end_at set) + const fourteenDaysAgo = new Date(FIXED_NOW_MS - 14 * 24 * 60 * 60 * 1000).toISOString(); + expect(getDaysRemainingInTrial(null, fourteenDaysAgo)).toBe(0); - // Organization created 35 days ago (expired 5 days ago, no free_trial_end_at set) - const thirtyFiveDaysAgo = new Date(FIXED_NOW_MS - 35 * 24 * 60 * 60 * 1000).toISOString(); - expect(getDaysRemainingInTrial(null, thirtyFiveDaysAgo)).toBe(-5); + // Organization created 19 days ago (expired 5 days ago, no free_trial_end_at set) + const nineteenDaysAgo = new Date(FIXED_NOW_MS - 19 * 24 * 60 * 60 * 1000).toISOString(); + expect(getDaysRemainingInTrial(null, nineteenDaysAgo)).toBe(-5); }); }); diff --git a/src/lib/organizations/trial-utils.ts b/src/lib/organizations/trial-utils.ts index 5df5bf27b..08c196fac 100644 --- a/src/lib/organizations/trial-utils.ts +++ b/src/lib/organizations/trial-utils.ts @@ -15,7 +15,7 @@ export function getDaysRemainingInTrial(freeTrialEndAt: string | null, createdAt if (freeTrialEndAt) { endDate = new Date(freeTrialEndAt); } else { - // Fallback to created_at + 30 days for backward compatibility + // Fallback to created_at + TRIAL_DURATION_DAYS for backward compatibility const created = new Date(createdAt); endDate = new Date(created.getTime() + TRIAL_DURATION_DAYS * 24 * 60 * 60 * 1000); } diff --git a/src/scripts/db/backfill-free-trial-end-at.ts b/src/scripts/db/backfill-free-trial-end-at.ts index 381fdafb2..3f43c093a 100644 --- a/src/scripts/db/backfill-free-trial-end-at.ts +++ b/src/scripts/db/backfill-free-trial-end-at.ts @@ -2,7 +2,7 @@ * Backfill script for free_trial_end_at column * * This script populates free_trial_end_at for existing organizations that have null values. - * It sets free_trial_end_at to created_at + 30 days for all organizations where it's currently null. + * It sets free_trial_end_at to created_at + 14 days for all organizations where it's currently null. * * Usage: * pnpm script src/scripts/db/backfill-free-trial-end-at.ts @@ -31,7 +31,7 @@ export async function run() { const updateResult = await db .update(organizations) .set({ - free_trial_end_at: sql`${organizations.created_at} + INTERVAL '30 days'`, + free_trial_end_at: sql`${organizations.created_at} + INTERVAL '14 days'`, }) .where(isNull(organizations.free_trial_end_at)); From 365fcf5e62207a54cdb1ad25d1e22f1390c0e2ba Mon Sep 17 00:00:00 2001 From: Alex Gold Date: Fri, 27 Feb 2026 14:59:39 -0500 Subject: [PATCH 2/2] Update src/scripts/db/backfill-free-trial-end-at.ts --- src/scripts/db/backfill-free-trial-end-at.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/db/backfill-free-trial-end-at.ts b/src/scripts/db/backfill-free-trial-end-at.ts index 3f43c093a..6e95bc389 100644 --- a/src/scripts/db/backfill-free-trial-end-at.ts +++ b/src/scripts/db/backfill-free-trial-end-at.ts @@ -31,6 +31,7 @@ export async function run() { const updateResult = await db .update(organizations) .set({ + // Changed from 30 days; no orgs on trial have null end at this time free_trial_end_at: sql`${organizations.created_at} + INTERVAL '14 days'`, }) .where(isNull(organizations.free_trial_end_at));