Relationships
#2618 End a collective's trial when it begins a paid subscription
Opened by skunk-ape · 9/28/2026
Today a collective trial is a start-only stamp. Nothing closes it when the collective starts paying: syncSubscription (lib/app/billing/sync-subscription.ts) never touches the collective, and the Collective aggregate has startTrial and extendTrial but no way to end a trial. The 30-day clock keeps running underneath a paid plan, and every consumer has to work out "paying" at read time to hide it.
Proposal: end the trial when a collective first gets an entitling subscription, so a paid collective holds no running trial clock at the source.
Research (from triaging #2541)
Can a paying collective start a new trial? No, not since #1852. Every trial trigger goes through startCollectiveTrial (lib/app/collective-trial.ts): collective created, serve/datastore token minted, extension marked private. It refuses grantsAccess() || everGrantedAccess. #1852 deliberately did no backfill, so stamps from before 2026-08-26 on paying collectives still exist. One accepted gap: the gate reads the webhook-synced subscription, so a token minted between checkout completion and webhook arrival still gets a trial.
Is the trial closed on subscribe? No. In #2541 the collective was created (trial collective_created, 2026-09-17), then subscribed to Team, and whoami JSON reported a live trial with endsAt. #2541 fixes only whoami, which reports trial: null for a paid plan.
Every consumer currently derives "paying" at read time:
| Consumer | How it handles a payer |
|---|---|
Web pages (/orgs, /o/[slug], /u/[username]) |
Hide the trial when the plan is paid |
| Admin collectives page | Hides it when paid |
Attio (deriveTrialStage) |
"Converted" when the collective ever paid |
trial-ended-watch |
Suppresses any collective that ever paid |
Paywall gate (private-extension-entitlement) |
Checks the subscription before the trial |
| swamp CLI text output | Hides it when the plan is paid |
| whoami JSON | Now hides it when the plan is paid (#2541) |
Each new consumer has to remember the rule. #1728 (a TRIAL ENDED modal shown to Team users) and #2541 are both instances of one forgetting it.
Design questions
- Where it happens: end the trial in the subscription sync when a snapshot first grants access. This is a cross-aggregate write (subscription and collective) in
lib/app. It needs to be idempotent under webhook replays and stale-event ordering. - Domain shape: a
Collective.endTrial(at)transition and a stored end/close stamp, versus truncating the end date. The stored start stays as history, andstartTrialmust still refuse (a trial is once per collective). - What
trialStatusreports afterwards: a closed trial must not read asexpiredto the trial-ended sweep, the "TRIAL ENDED" UI or Attio "Expired/Lapsed". The state name needs a decision; "converted" was rejected in #2541 triage. A paid collective is paid/current, or later overdue. - Churn: today a collective that cancels inside its 30 days falls back to its trial (see #2606). Ending the trial at subscribe time removes that fallback, which is probably the intended outcome.
- Backfill: close the trial on existing collectives that are paying now or ever paid, including stamps from before #1852. This is a migration, not an
INSERT…SELECT, but it still needs one run across replicas. - Checkout race: a trial stamped in the checkout/webhook window would be closed by the same sync when the webhook lands.
- Harness:
scripts/harness-collective-trials.tscomps subscriptions withsubscriptionRepo.saveand openscollective_createdtrials by callingstartCollectiveTrialdirectly, so it never runs the real sync or create path, and it never looks at whoami. Extend it to pay throughsyncSubscription, open trials throughcreateCollective, print whoami per row, and add past_due, cancelled-inside-window, token-after-pay and checkout-race rows.
Related
#2541 (whoami fix), #2606 (a cancelled collective regains trial access), #1852 (no trial for payers), #1728 (TRIAL ENDED modal for Team users), #1544 (trial in whoami).
Open
No activity in this phase yet.
system commented 9/28/2026, 8:35:46 PM
Classified automatically when this issue was filed.
- Source: Swamp Club
If you feel this classification is incorrect, add a ripple to tell us so.
Sign in to post a ripple.