Skip to content

[review-only] Subscriptions: Phase 6 UI, suspend audit log, tier guard, calCom server guard#1

Open
ericmaster wants to merge 7 commits into
phase7-review-basefrom
nimblersoft
Open

[review-only] Subscriptions: Phase 6 UI, suspend audit log, tier guard, calCom server guard#1
ericmaster wants to merge 7 commits into
phase7-review-basefrom
nimblersoft

Conversation

@ericmaster

Copy link
Copy Markdown

Review-only PR — these 6 commits are already on `nimblersoft` (mainline). `phase7-review-base` is a frozen marker at the pre-work tip so the diff shows exactly this work. Do not merge — close after review.

What's here (submodule code for the nimblerbot billing layer)

  • Phase 6 (fd049fa2c) — customer billing UI (features/billing/components/nimblerbot/) + authz hardening (assertWorkspaceMember, subscription.getAiUsage) + reworded /past-due.
  • GAP-01 (f459ab83d, c6c57b5d5) — persist suspendReason + new append-only WorkspaceAuditLog (migration 20260620000000); setWorkspaceSuspension() writes reason + audit entry in one tx; synced workspaces Zod schema.
  • GAP-02 (1c2f32d75) — guard changeTier against missing/CANCELED subscriptions (BAD_REQUEST).
  • GAP-03 (cdece58b2) — expandable per-invoice payments sub-row in admin InvoicesPanel (method/providerRef/confirmedAt).
  • Phase 1.2 (af77e3cfd) — server-side assertBlocksEntitled(plan, groups) closing the calCom sidebar-only gate; wired into update/import/publish handlers.

Hard constraints kept

  • No live payments wired. Phase 3.4 (DeUna webhook/route) stays unwired; nimblerbotBillingConfig.ts placeholders untouched; "Pay with DeUna" only renders when paymentLink is set.

Verification

  • @typebot.io/subscriptions: 49 pure + 59 DB-backed tests green; builder + subscriptions typecheck green; biome clean.

Docs (CONTEXT.md glossary, ADRs 0002–0004, AGENTS.md ops runbook) live in the parent nimblerbot repo PR.

ericmaster and others added 6 commits June 20, 2026 03:36
Replace inherited Stripe billing tab with the Nimblerbot subscription UI
(summary, AI usage bar, invoices with line items/payments, plan cards,
bank-transfer + DeUna pay actions) gated by isNimblerbotInstance(). Add
customer subscription.getAiUsage route and assertWorkspaceMember authz on
getByWorkspace/getAiUsage/invoice.list. Reword /past-due for the 7-day
grace + bank-transfer/DeUna flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…P-01)

Add suspendReason String? to Workspace and a new append-only
WorkspaceAuditLog model (migration 20260620000000). Extract suspend/
unsuspend into setWorkspaceSuspension(), which persists the reason and
writes a SUSPEND/UNSUSPEND audit entry (actor id + email) in one
transaction; unsuspend clears the reason while the log keeps history.
DB-backed tests cover persistence, reactivation, and append-only history.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The in-place tier-change path (subscription.changeTier + SubscriptionForm
selector for active subs) already satisfies GAP-02. Close it by hardening
changeTier to reject a missing/CANCELED subscription — those must be
re-activated, mirroring the admin UI which routes them to the activation
form. Adds a BAD_REQUEST guard inside the transaction + a DB test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The workspaceSchema must satisfy z.ZodType<Prisma.Workspace>; the new
suspendReason column made it incomplete and broke builder typecheck.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each invoice row with payments now shows an expandable sub-row listing
each Payment's method, status, amount, providerRef, and confirmedAt — the
data was already loaded via include: { payments: true } but never rendered.
Staff no longer need to query the DB to see how an invoice was paid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e 1.2)

The Phase 1.2 calCom gate was client-sidebar-only, so a non-entitled
workspace could still add a cal-com block via a pasted or imported flow.
Add assertBlocksEntitled(plan, groups) which scans a flow's groups for
feature-gated forge blocks (cal-com -> calCom feature) and throws
FORBIDDEN if the plan lacks the feature. Wired into the persistence seams
that bypassed the sidebar: handleUpdateTypebot (paste/edit),
handleImportTypebot (import/duplicate/template), and handlePublishTypebot
(defense-in-depth). Pure-logic tests included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ericmaster

Copy link
Copy Markdown
Author

@claude review

Hardcoded contact/bank details in nimblerbotBillingConfig.ts would have
landed in the public fork's git history once real values were filled in.
Move all 7 fields to NEXT_PUBLIC_NIMBLERBOT_BILLING_* env vars declared
in the central @typebot.io/env package and document them in .env.example.
The config file now reads from env with empty-string fallbacks so the
billing UI gracefully renders nothing when the vars are unset.

Also fix a pre-existing timing flakiness in @typebot.io/results rpc.test.ts:
the stream test was sensitive to parallel-load fiber scheduling. Widened the
initial startup sleep (5ms→50ms) and workflow mock sleep (50ms→300ms) to
ensure the stream fiber is consuming before items are offered, with a clear
separation between progress emission and workflow completion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant