Skip to main content
← Back to list
01Issue
FeatureOpenSwamp ClubPublic
AssigneesNone

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, and startTrial must still refuse (a trial is once per collective).
  • What trialStatus reports afterwards: a closed trial must not read as expired to 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.ts comps subscriptions with subscriptionRepo.save and opens collective_created trials by calling startCollectiveTrial directly, so it never runs the real sync or create path, and it never looks at whoami. Extend it to pay through syncSubscription, open trials through createCollective, print whoami per row, and add past_due, cancelled-inside-window, token-after-pay and checkout-race rows.

#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).

02Bog Flow
◉OPEN○TRIAGED○IN PROGRESS○SHIPPED

Open

9/28/2026, 8:35:46 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

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.