Skip to content

Add a 14-day no-card free trial and replace the Free banner with timely notices - #2686

Open
ejsmith wants to merge 26 commits into
mainfrom
feature/free-trial
Open

ejsmith wants to merge 26 commits into
mainfrom
feature/free-trial

Conversation

@ejsmith

@ejsmith ejsmith commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

What changed

New hosted signups get a 14-day free trial with Medium features (including Exie) for their first organization, with no card. When the trial ends, the organization moves to the Free plan. There's no permanent nag: users see a one-time notice when the trial ends, and every plan gets a dismissible warning at 80% of its monthly events.

Plans and trial

  • New hidden EX_TRIAL plan (Medium limits, premium features, Exie). Organization.TrialEndsUtc / TrialReminderSentUtc and User.TrialStartedUtc record trial state.
  • Trials are for new users only: no organization memberships and no organizations created. One trial per mailbox: a SHA-256 of the normalized email (lowercase, no +tag, no Gmail dots) is recorded in a new trial-claims index and checked under a per-mailbox lock. The claim is separate from the user, so deleting the account and signing up again doesn't bring the trial back.
  • Disposable email domains get Free instead of a trial. System admins edit the blocked list on System → Settings; a built-in list applies until they save their own.
  • A user can own only one Free or trial organization. New organizations record an immutable CreatedByUserId; existing organizations keep none and fall back to the membership rule. Creation and downgrades to Free are serialized per owner.
  • Hourly TrialExpirationJob (in-process cron + new k8s CronJob, TrialExpiration job arg) sends a "trial ends soon" email 3 days before the trial ends to verified members once, then moves expired trials to Free.
  • Upgrading during a trial uses the existing change-plan flow and bills immediately. Choosing Free ends the trial. Stripe is not involved in the trial.
  • Admins can put a Free or trial organization on EX_TRIAL to start or extend a trial; admin plan changes take the organization billing lock.
  • User and project limits are removed from every plan (max_users / max_projects remain in the API as -1).

Abuse and cost controls

  • Inviting from a Free or trial organization requires a verified email and is limited to 10 per user per UTC day (hosted only). Paid organizations are unchanged.
  • Exie on trials requires a verified email and an unexpired trial, has a $1 monthly cap, and shares a combined daily cost cap across all trial organizations ($25 by default). The cap counts in-flight requests and rejects requests that start once it's reached, so a burst can go over by at most one request's estimate.
  • Admin plan stats count trial, converted, and expired-trial accounts; the paid organization filter excludes trials.

UI

  • During a trial, the sidebar organization header shows a quiet "N days left in trial" item that opens the change-plan dialog. The trial banner only appears in the last 3 days and can be dismissed until the next day.
  • When a trial ends without subscribing, a one-time notice says the organization is on Free and lists its real limits (events/month, retention, no Exie/alerts/integrations). Dismissing it records trial-ended-<organizationId> with the existing product-tour record endpoint, so it doesn't come back for that user on any device. It's not shown while a global admin impersonates.
  • The permanent Free banner is removed. Contextual upgrade prompts (Exie upgrade card, premium page notice) and the suspension and overage banners are unchanged.
  • New on every plan: a dismissible warning at 80% of the current month's event limit (from the organization's existing usage, bonus events included). A dismissal lasts for the rest of the UTC month; the 100% overage banner is unchanged.
  • Banner priority: suspended > monthly overage > hourly overage > request limit > project configuration > premium upgrade > 80% usage > trial (last 3 days) > trial ended (once) > no projects.
  • Change-plan dialog understands trials: shows days left, preselects Medium, notes billing starts today; user counts removed from plan rows.
  • Exie panel shows a verify-email state for unverified trial users. Verifying in another tab unlocks it in every open tab.
  • Following a verification link while signed in as a different account now says which account was verified and which one is signed in.

Configuration (all optional): Billing:TrialDays (14, 0 disables trials), Billing:TrialReminderDays (3), Billing:TrialAssistantDailyCostLimitUsd (25), Billing:FreeInviteDailyLimit (10).

Affected APIs

Additive: trial_ends_utc on organizations, email_verification_required on Exie access, trial counts on plan stats, and GET /api/v2/users/verify-email-address/{token} now returns { "email_address" } for the account it verified. New admin endpoints: GET/PUT /api/v2/admin/trial-settings. Creating projects and inviting users no longer return 426 for plan limits. openapi.json and generated client models are updated.

Deployment

  • New trial-claims index, created on startup like the others.
  • Migration 11 maps created_by_user_id and sets existing organizations to unlimited users and projects. It doesn't guess creators for existing organizations. No manual step is needed.

Verification

  • dotnet test: 3613 total, 0 failed, 27 skipped. New tests cover trial start and one-trial rules, trial upgrade and downgrade, invite verification and daily limits, the expiration job lifecycle, the trial email, the Exie trial gate, and the shared daily cost cap.
  • npm run validate and vitest: 1188 passed. New tests cover the 3-day trial banner and its dismissal, the one-time trial-ended notice (including another device, a failed save, and impersonation), the 80% usage warning and its priority, and the dismissal periods. Backend tests cover the 14-day and 3-day defaults.
  • Manual QA against a standalone API and job (isolated scope, shared containers): trial signup, second-organization 426, unlimited projects, invite 403/429, trial → Free, Exie verify-email gate, and the TrialExpiration job reminder and expiry all behaved as expected.
  • Banners and the trial change-plan dialog checked in Storybook.
  • Manual QA of the notice rework in the running app (standalone API with an isolated scope + Vite): signup gets a 14-day trial with only the sidebar indicator; 2 days left shows the banner, and a dismissal survives a reload; after the trial ends the one-time notice shows real limits and stays gone after a reload and with localStorage cleared; 85% usage shows a dismissible warning; a Free organization that never trialed shows no banner.
  • Independent reviews and several Codex review rounds are addressed in follow-up commits. One open thread (the shared trial Exie cap resets if Redis restarts mid-day) is left for a maintainer decision.

Breaking changes

Inviting from a Free or trial organization can now fail where it used to succeed:

Case Before Now
Inviter's email isn't verified 200 403
More than 10 invites today 200 429
Paid organizations unchanged unchanged

The old 426 for the per-plan user limit is gone because plans no longer limit users. This was a deliberate decision to curb invite spam from throwaway signups.

A user's first organization now starts on a hidden EX_TRIAL plan with
Medium features for 7 days, then moves to the Free plan, which keeps the
non-dismissible upgrade banner. Upgrading during the trial uses the
existing Stripe flow and bills immediately; Stripe is not involved in
the trial itself.

- Remove user and project limits from every plan
- Require a verified email and a daily limit to send invites
- Hourly TrialExpirationJob sends a reminder email and ends trials
- Exie on trials needs a verified email, a $1 monthly cap, and a
  shared daily cost cap across all trial organizations
- Trial banner, post-trial Free banner, and trial-aware plan dialog
- Billing:* settings, including TrialDays (0 disables trials)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-08T15:17:50.401730Z daba9fb New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 388b4f8f73

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ejsmith and others added 2 commits October 22, 2026 08:40
- Count only organizations a user owns for the one-free-organization
  rule and trial eligibility, so invited members can create their own
  organization (and get a trial)
- Deny Exie for trial organizations whose trial has ended, without
  waiting for the expiration job
- Count in-flight Exie reservations against the shared trial daily cap
- Track free and paid invite allowances separately
- Migrate existing organizations to unlimited users and projects
- Show an ended-trial banner before the job moves the organization
- Use an invariant date in the trial reminder email
- Remove byte order marks added by earlier edits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The banner called toLocaleString() on max_events_per_month, which threw
when an organization response omitted it and broke the app shell (caught
by the navbar-layout E2E test, whose mocked organization has no limits).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a415af16f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

An organization that converted from a trial keeps TrialEndsUtc, so a
later downgrade to Free showed "Your trial has ended". Show that only
when the organization has a trial date and never subscribed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23330d590a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Take a per-user lock around organization creation and reload the user
  inside it, so concurrent requests can't both pass the one-trial and
  one-free-organization checks
- Add Organization.CreatedByUserId and use it for ownership, falling
  back to BillingChangedByUserId for existing organizations, so a member
  changing the plan doesn't transfer ownership

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 33a2358a02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Query owned organizations by CreatedByUserId (now mapped), so leaving
  an organization doesn't free up its owner's free-organization slot
- Check Free downgrades against the organization's owner, not the member
  making the change
- Map created_by_user_id in migration 11 (renamed
  UpdateOrganizationsForTrials)
- Document the invite endpoint's 403 and 429 responses and drop the
  removed 426 in the endpoint metadata and OpenAPI snapshot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a5e3e05bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Ask for exactly what each check needs instead of loading the first 100
created organizations: a count for "owns any organization", and a query
filtered to the free and trial plans for the one-free-organization rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dee3aa9af1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ejsmith and others added 2 commits October 22, 2026 09:35
- GetBillingPlanStatsAsync read only the first page of organizations,
  so every counter (including the new trial ones) undercounted on
  larger installs; page through all organizations
- If adding the creator's membership fails after the organization is
  saved, remove the organization so it doesn't count against the user's
  free-organization limit while being unreachable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Page through all organization members (user limits are gone, so
  members can exceed one page)
- Record TrialReminderSentUtc only after every reminder is queued, so a
  queueing failure is retried on the next run instead of silently
  skipping the reminder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a44851e611

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Downgrades to Free now take the same per-owner lock as organization
creation, so two concurrent downgrades of one owner's paid organizations
can't both pass the one-free-organization rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 912f680930

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Migration 11 now copies BillingChangedByUserId into CreatedByUserId for
existing organizations, so ownership always uses the immutable creator.
This removes the membership-based fallback, which read the mutable
billing audit field and changed owner whenever a member changed the
plan. Sample data records the creator too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c334ab1d7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Admin plan changes preserve the billing price; applying EX_TRIAL reset it
to zero, which a later admin change back to a paid plan didn't restore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 611e15787b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The shared daily counter is updated after durable usage is recorded; if
that cache update threw, the provider lifecycle treated the request as
unreconciled and recorded the estimate again. Log and continue instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9463ad644d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Moving a paying organization onto the no-card trial left its Stripe
subscription running and then dropped it to Free when the trial ended,
losing its price. The admin trial action is for starting or extending a
trial, so restrict it to free and trial organizations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48b13524c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ejsmith and others added 2 commits October 22, 2026 10:40
Admin plan changes now share the organization billing lock with user
plan changes and the trial expiration job, and reload the organization
inside it, so an admin extending a trial can't race the job ending it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba300caeab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ejsmith ejsmith changed the title Add a 7-day no-card free trial for new organizations Add a 14-day no-card free trial for new organizations Oct 23, 2026
Show the trial banner only in the last 3 days, dismissible until the next day,
with a quiet days-left indicator in the sidebar. Replace the permanent Free
banner with a one-time trial-ended notice recorded per user and organization.
Add a dismissible warning at 80% of the monthly event limit, and send the trial
reminder email 3 days before the trial ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d8644f109

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Save the trial-ended dismissal to the cached user right away and reload it if
the save fails, skip the notice while impersonating so admins don't fill their
own product tour records, and re-check day and month dismissals each minute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ejsmith ejsmith changed the title Add a 14-day no-card free trial for new organizations Add a 14-day no-card free trial and replace the Free banner with timely notices Oct 23, 2026
Check the shared daily total after reserving a provider request's estimate and
reject requests that started at or past the limit, so a burst can exceed it by
at most one estimate. Only reservations whose estimate was counted adjust the
shared total later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 00e2cd7571

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
public Task<ILock> AcquireOwnerFreeOrganizationLockAsync(string userId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(userId);
return _lockProvider.AcquireAsync($"User:{userId}:free-organization", BillingLockDuration, TimeSpan.FromSeconds(30));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be on the organization level since you could have multiple users? Or is this saying that an organization is only created by one user so the trial is by that person, and if anyone changes billing at all then this trial lock is cancelled. What if a user creates multiple free organizations? I guess this is a free trial for a given free organization, but what about all other premium features? Guess exie is an exclusion to that and that makes sense.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's per user on purpose. The rule it protects is "one Free or trial organization per owner", so it serializes one owner's creates and downgrades to Free; changes to a single organization still take that organization's billing lock. A trial applies to the user's first organization only, and any further organizations start on Free. The trial plan gives that organization Medium limits and premium features, Exie included, until it ends.

}

private static string GetSharedDailyCostKey(string sharedDailyCostLimitKey, string dayId)
=> $"shared:{sharedDailyCostLimitKey}:usage:{dayId}:cost-microdollars";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is a microdollar and why does that matter here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A microdollar is a millionth of a dollar. Exie cost is tracked with integer cache increments, so it's stored as long microdollars to avoid floating-point drift. It's the same unit as AssistantUsageInfo.CostInMicrodollars on main; ToMicrodollars converts the configured USD cap for the comparison.

niemyjski and others added 2 commits November 3, 2026 12:25
- One trial per mailbox: store the normalized email (lowercase, no +tag,
  no Gmail dots) that claimed a trial and check it under a per-mailbox lock.
- Disposable email domains get Free instead of a trial. System admins edit
  the blocked list on the system settings page; the built-in list applies
  until they save their own.
- Trials are only for users who belong to no organization and created none.
- Migration 11 no longer guesses creators from the last billing change.
  Organizations without a recorded creator fall back to the membership rule.
- Invite verification and the daily limit apply only to Free and trial
  organizations; paid organizations invite as before.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 88b1f43700

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@niemyjski niemyjski left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alias normalization and legacy membership fallback address the original alias and guessed-creator cases, and paid invitations now retain their previous behavior. Local revalidation still reproduced account-deletion trial renewal and both verification issues below.

Validation: 217 focused backend tests and 31 frontend tests passed; local staging-mode API and browser checks used Mailpit and a simulated Exie provider. Actual Stripe checkout and live AI were not exercised.

The existing mailbox-claim review finding is confirmed: start a trial, delete its organization and user, register the identical address again, and create an organization; the replacement receives EX_TRIAL again. Keep the claim independently of the deletable user record.

Other open feedback remains relevant: TrialExpirationJob still catches cancellation inside the batch and can page/renew afterward; the migration test does not check preservation of unrelated mappings/settings; and the invitation cache key remains constructed inline. Paid invite compatibility is fixed, but Free/trial invites still introduce 403/429 for formerly accepted requests, so the compatibility decision remains open while the PR says there are no breaking changes.

@exceptionless exceptionless deleted a comment from CLAassistant Nov 5, 2026

@niemyjski niemyjski left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested for two reproduced blockers:

  • Account deletion resets the mailbox trial claim, allowing the same address to receive another trial. Preserve that claim independently of the deletable user record.
  • Verification in another tab leaves the original Exie panel locked. Refresh the separate assistant-access query through the existing account-change path.

Before merge, explicitly decide whether the new Free/trial invite 403/429 responses are accepted compatibility changes and describe that decision accurately. The anti-abuse restrictions may be intentional; this is not a request to remove them.

Follow-up, not release blockers: misleading success text when another account's verification link is used, expiration-job cancellation handling, and bounded-memory billing statistics (downgraded to P3). No broad refactor is requested.

Verification: reviewed head 88b1f4370. On November 6, 35 focused backend tests, 31 frontend tests, and diff whitespace checks passed. Browser/API reproductions are from November 4 on that same head; E2E, Stripe checkout, and live AI were not rerun.

- Keep mailbox trial claims in a separate trial-claims index (hashed), so deleting and recreating an account doesn't renew the trial
- Refresh Exie access in every tab when the signed-in user changes, and say which account a verification link verified
- Stop the trial expiration job before the next organization when cancelled; read organizations from cache
- Move the free invite counter and billing lock keys into BillingManager
- Use BillingPlan.Unlimited, require TimeProvider in AssistantAccessService, and share one usage date helper
- Accumulate billing plan stats page by page
- Test that migration 11 keeps existing mappings and settings; add Arrange/Act/Assert to the PR's frontend tests
@ejsmith

ejsmith commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

Addressed the review in bf287f7:

  • Account deletion renewing the trial: mailbox claims now live in a separate trial-claims index (SHA-256 of the normalized address), so deleting and recreating the account gets Free.
  • Verification: Exie access refreshes in every open tab on UserChanged. The verify endpoint returns the verified email_address, and the page names both accounts when they differ.
  • TrialExpirationJob: stops before the next organization once cancelled, and reads from cache.
  • Migration test: checks existing mappings and index settings are kept.
  • Invite cache key: moved into BillingManager with the other key helpers.
  • Billing stats: accumulate page by page.
  • Invites (decision): paid organizations are unchanged. Free and trial organizations keep the verified-inviter 403 and 10/day 429 to curb invite spam. The description now lists this under Breaking changes instead of "None".

Replied on every thread. The ones that were questions (lock scope, 426, microdollars, Mailer, invite limit setting) are left open for you.

@ejsmith
ejsmith requested a review from niemyjski November 7, 2026 22:09
Persist mailbox claims before granting trials, reject unconfirmed shared cost reservations, patch only migrated organization limits, and refresh sidebar countdowns. Add regression coverage for failure recovery, usage reconciliation, concurrent writes, and elapsed time.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: daba9fb6d4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

name: 'Exie upgrade to Medium'
};

/** Trial org — shows days left, preselects Medium, and notes that billing starts today. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Spell out organization in the trial story comment

The new comment abbreviates organization as org, contrary to the explicit rule in root AGENTS.md, which also applies to comments. Replace “Trial org” with “Trial organization” to satisfy the documented convention.

AGENTS.md reference: AGENTS.md:L78-L78

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.AppHost 23% 22% 130 ❌
Exceptionless.Core 78% 70% 11111 ✔
Exceptionless.Insulation 51% 43% 370 ➖
Exceptionless.Web 87% 72% 9547 ✔
Summary 81% (29036 / 35954) 70% (14511 / 20707) 21158 ✔

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants