docs(licensing): align ThreadPlane branding + add /docs/licensing landing#532
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…ing page Editorial cleanup across all licensing-touching surfaces: - New /docs/licensing landing page (linked from email + footer + /thanks) - Email copy: "Thanks for subscribing" → "Thanks for your ThreadPlane license purchase"; CACHEPLANE_LICENSE → THREADPLANE_LICENSE in installation snippet; install snippet now shows provideChat() pattern (the actual API), not process.env-only - Brand: "Threadplane" → "ThreadPlane" in customer-visible copy and library NOTICE/README files - License name: always "ThreadPlane Commercial license" (capitalized) - Tense: drop "Starting with the next published version" from COMMERCIAL.md and root README — the license is live - Eval policy: precise "30 calendar days from first commercial use, good-faith, no telemetry" wording in libs/chat/COMMERCIAL-USE.md and PricingFAQ - Tier scoping: standardize "Indie" name, prefix paid features with "ThreadPlane Commercial license", drop Enterprise "starting at $10k/year" display period (was inconsistent with "Custom") - Footer "Licensing" link → /docs/licensing (was /pricing#faq) - /thanks page button → /docs/licensing (was /docs/chat/...) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pricing grid:
- All 5 tier cards fit one row at desktop (auto-fit minmax 200px → 5-up
at ≥1100px, gracefully wraps below)
- Tighter padding, smaller price (32px vs 40px)
- Inline price + period ($299/dev/yr)
- Per-card subtitle ("per developer", "one app · one dev")
- "MOST POPULAR" pill badge floating above the highlighted Dev Seat card,
with a soft blue glow
- "Best for: ..." moved out of the feature bullet list into its own row,
separated by a hairline
- "ThreadPlane Commercial license" bullet removed from every card,
replaced by a single disclaimer below the grid
- CTAs tightened: "Buy indie license" → "Buy Indie",
"Buy developer seat" → "Get Developer Seat",
"Contact sales" → "Talk to Sales"
- Features tightened to 3 lines per card
Licensing FAQ:
- Now uses the shared <FAQ> component (chevron-style accordion)
- Centered heading + "Questions" eyebrow to match the home FAQ section
- Surface switched from canvas → white for visual continuity with home
Tier config (pricing/tiers.config.ts):
- Added `subtitle` and `bestFor` fields (display-only; no Stripe impact)
- Renamed "Community / Noncommercial" → "Community"
- Dropped "starting at $10k/year" from Enterprise display period
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Page-level changes: - H1 → "Simple, transparent pricing" (drop "Pricing for production AI chat interfaces" and the lede paragraph below) - Hero section uses tight vertical rhythm to pull the table closer - Remove the "A license is required when @ngaf/chat is used in a commercial product..." SmallNote — redundant with the table - Remove the redundant "Commercial evaluation is free for 30 days" note below the table — the table already shows a "30-day commercial eval" row - PricingGrid component removed from page (file kept for now in case the card layout is wanted elsewhere) CompareTable (rewritten to be the primary pricing display): - Header row: tier name + MOST POPULAR pill on Dev Seat - Sub-header row: price + period inline ($299 /dev/yr) - Feature rows pull all data from the same set we used in the grid plus License, Commercial production use, SLA, Security review - Footer row: per-column CTA button (outlined "secondary" for non-highlight tiers; solid "primary" for Dev Seat — more visually apparent than the prior ghost variant) - Dev Seat column tinted via tokens.surfaces.surfaceTinted - Disclaimer below table: "All paid tiers include the ThreadPlane Commercial license · One-time annual payment · 12-month validity" Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove "What happens to older MIT versions?" from the pricing FAQ - Remove the bottom-of-pricing "Because commercial use requires a license, @ngaf/chat is source-available rather than OSI open source..." footnote - libs/chat/CHANGELOG.md: present-tense license entry; drop the historical MIT grandfathering line - PricingFAQ.spec: question count drops to 6; assertion now reads the question span (the shared FAQ component renders a chevron next to it which the old textContent assertion didn't account for) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rise
Lead form (value-prop + qualifying form direction):
- Headline: "Built for procurement. / Backed by delivery."
- Subtitle: "Volume licensing, custom contract, and optional concierge
delivery — so your first Angular agent ships, not just compiles."
- Side-by-side layout at wide widths: value props on the left, form on
the right. Stacks vertically on narrow widths.
- Four value-prop tiles. The Pilot-to-Prod tile is highlighted with the
accent border + tinted background, plus a deep link to /pilot-to-prod.
- Form gains qualifying fields:
- Team size (select: 1-5 / 6-25 / 26-100 / 100+ developers)
- Timeline (select: This quarter / Next quarter / 6+ months / exploring)
- Pilot-to-Prod interest (radio: Yes, include it / Tell me more /
License only) — sent as `pilot_interest` in the lead payload so
sales can route accordingly
- Company is now required (was optional) — enterprise leads always have
a company
- CTA: "Request enterprise quote" (was "Get in touch")
Compare table: new "Pilot-to-Prod engagement" row, marked "Optional" for
Enterprise and "—" for everything else. Makes the bridge visible without
having to scroll to the lead form.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tion Licensing section: - Drop License row (license name is implicit from the tier) - Drop Security review (folded into Enterprise's value-prop block instead) - Drop Dev · staging · prod (implicit for all paid tiers) - Rename 'Commercial production use' to 'Commercial' - Enterprise Support → 'Slack Connect' (was 'Priority + private channel') - Pilot-to-Prod row value for Enterprise → 'Weekly 30-min check-in' (was 'Optional'). All non-Enterprise tiers show — What's in the box section (new): - Headless chat primitives - Durable threads - Interrupts (human-in-the-loop) - Subagents + delegation - Planning + memory - Generative UI (json-render + A2UI) - Signal-based streaming - Citations + sources panel - LangGraph + AG-UI adapters - Theme presets All tiers ✓ — Community gets noncommercial use, paid tiers get commercial. CTAs in header AND footer: - The Plan/Price header rows now have a third row with the CTA buttons, so the buyer can act without scrolling past the table - tfoot keeps its CTA row so long scrolls still have an action close at hand Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier ladder is now: Community · Developer Seat · Team · Enterprise.
Why:
- Per-app pricing ("App Deployment") was confusing — what counts as an
app, a domain, a URL? — and hard to enforce. Dropped entirely.
- Indie was a fence-sit between "free for solo OSS" and "Developer Seat"
— Developer Seat covers solo developers too, so Indie is redundant.
- Team is the new bulk/procurement-friendly SKU: 5 developer seats at a
flat $1,495/yr (= 5 × $299, no discount, no premium). The value is
single SKU + single renewal + procurement convenience.
Comparison table:
- Drop "Commercial apps" row (no per-app pricing means the column was
meaningless)
- Rename "Commercial production use" → "Commercial"
- Drop "License" + "Security review" + "Dev · staging · prod" rows
- Enterprise Support → "Slack Connect"
- Add "Pilot-to-Prod" row → Enterprise = "Weekly 30-min check-in"
- New "What's in the box" section listing core library capabilities
- CTAs in BOTH header and footer of the table
Enterprise pricing:
- Display "From $4,000/mo" (was "Custom annual"). Sales-led; 8-week
Pilot-to-Prod engagement with weekly 30-min check-ins is included.
Backend changes:
- libs/licensing LicenseTier = 'developer_seat' | 'team' | 'enterprise'
(dropped indie + app_deployment from the union)
- apps/minting-service MintableTier = 'developer_seat' | 'team';
computeSeats('team', _) returns 5
- apps/website checkout/session route BUYABLE_SLUGS = { developer_seat,
team }
- Analytics events: pricing_tier_indie, pricing_tier_app_deployment
replaced by pricing_tier_team
- pricing/tiers.generated.ts: indie + app_deployment price IDs removed.
Team price ID needs sync-products.ts run before /pricing checkout will
succeed for Team — falls back to a 503 with a helpful message until
then. Developer Seat price ID still valid.
- /docs/licensing tier scoping table updated (3 rows: Dev Seat / Team /
Enterprise)
- PricingGrid.tsx deleted (unused after the table-as-pricing pivot)
Test updates:
- minting-service tier.spec, handlers.spec, email.spec, sign.spec,
route.spec all updated to use developer_seat/team fixtures
- libs/licensing sign-license.spec updated
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…As below each
- Remove "End users" row (was Unlimited on every tier — no signal)
- Community Support: "Community" → "GitHub"
- Developer Seat Support: "Email" → "GitHub" (matches Community —
paid email support starts at Team)
- Split the single comparison table into TWO stacked tables:
- Section 1: "Licensing" header, columns include price row, rows
cover Commercial / Developers / 30-day eval / Support / SLA /
Pilot-to-Prod
- Section 2: "What's in the box" header, no price row repeated,
rows cover all ✓ library capabilities
- CTAs now sit BELOW each section as a separate CSS grid, aligned with
the table columns but visually outside the table boundary. 24px gap
between table and CTA strip; 56px gap between the two sections.
- Drop the in-header CTA row and the tfoot CTA row from the prior
single-table layout — replaced by the strip pattern.
- Drop the "One-time annual payment · 12-month validity" disclaimer
while subscription pricing is being planned (will return with new
copy after the Stripe refactor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pricing model is now recurring subscriptions, with both monthly and annual
cycles. Annual saves ~15% vs 12× monthly (Developer Seat: $29/mo or
$299/yr; Team: $149/mo or $1,495/yr). Enterprise is unchanged (from
$4,000/mo, sales-led).
This PR covers the WEBSITE-VISIBLE half of the migration. A follow-up PR
will refactor the minting service from `checkout.session.completed` to
subscription events (created/updated/deleted, invoice.paid), wire token
expiry to current_period_end, and implement the grace-until-period-end
cancellation behaviour.
Schema (pricing/tiers.config.ts):
- TierPrice = { cents, display, period }, one per cycle
- TierConfig.prices: Record<BillingCycle, TierPrice>
- Removed displayPrice/displayPeriod/priceCents (replaced by .prices)
- Helpers: annualSavingsDollars(tier), annualDiscountPercent()
UI (apps/website/src/components/pricing/CompareTable.tsx):
- Monthly | Annual toggle pill above the table; Annual is default
- Toggle label dynamically shows "save N%" pulled from Team's discount
- Toggling flips the Price row across all paid columns; Community + Enterprise
stay constant
- CtaStrip receives cycle and includes <input name="billing_cycle"> in the
Stripe POST so the server picks the right price
Checkout route (apps/website/src/app/api/checkout/session/route.ts):
- mode: 'payment' → 'subscription'
- Accepts `billing_cycle` (default 'annual')
- STRIPE_PRICE_IDS shape changes to { tier: { monthly, annual } }
- subscription_data.metadata carries ngaf_tier_slug + ngaf_billing_cycle
forward so the (future) minting service can route on either field
- Returns 503 with a helpful message if the price ID for the requested
(tier, cycle) hasn't been synced yet
Stripe sync (scripts/stripe/sync-products.ts):
- Creates BOTH monthly and annual recurring prices per buyable tier
- Idempotent: matches by (interval, unit_amount); archives stale prices
- Generated file shape updated to the nested { monthly, annual } map
Docs (apps/website/src/app/docs/licensing/page.tsx):
- Tier table shows both monthly and annual prices
- Disclaimer rewritten: "Paid tiers are recurring subscriptions. Annual
saves ~15% vs monthly. Cancel anytime — the license stays valid through
the end of the current paid period."
Tests (route.spec.ts):
- Updated STRIPE_PRICE_IDS mock to the nested shape
- New assertions: default cycle is annual; billing_cycle=monthly routes to
the monthly price; invalid billing_cycle returns 400; Team routes to the
team annual price by default; subscription mode is used
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /angular landing page actually documents the @ngaf/langgraph adapter library (eyebrow already says "@ngaf/langgraph"), so the URL was misleading. - apps/website/src/app/angular/ → apps/website/src/app/langgraph/ - page.tsx metadata pathname → '/langgraph' - solutions/[slug] LIBRARY_HREF.Agent → '/langgraph' - Footer Libraries column "Angular" link → "LangGraph" pointing at /langgraph Untouched (intentional): - GitHub repo URL cacheplane/angular-agent-framework — repo name, not a route - EcosystemStrip "Angular" entry — about Angular the framework - WhitePaperBlock paper="angular" id — references the streaming whitepaper - Eyebrow tone="angular" / Pill variant="angular" — design tokens Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vents The website checkout now opens Stripe sessions in `mode: 'subscription'`, so the minting service has to follow suit. This drops the one-shot `checkout.session.completed` flow and wires the handler to the subscription lifecycle. Schema - Rename `licenses.stripe_payment_id` → `licenses.stripe_subscription_id` (migration 0002, snapshot + journal updated). - Add `getLicensesByCustomerId` query for customer-keyed lookups. Handlers (apps/minting-service/src/lib/handlers.ts) - `customer.subscription.created`: mint a license, expiresAt set to `subscription.current_period_end`, email the token. - `customer.subscription.updated`: re-mint only when seat count or period_end actually change. Status transitions to canceled/unpaid do NOT immediately revoke — the license expires naturally at current_period_end. - `invoice.paid` (`billing_reason: subscription_cycle`): renewal — fetch the subscription, re-mint with the new period_end, email new token. The first-time `subscription_create` invoice is skipped (handled by `customer.subscription.created`). - `charge.refunded`: keep revoking immediately, but key off `charge.customer` since we no longer track payment_intent → license. Revoke every active license for that customer. Tier metadata is read from `subscription.metadata.ngaf_tier_slug` first, with the price metadata as a fallback. Seats default to `items[0].quantity` (the `team` tier still overrides to 5 via `computeSeats`). Tests - handlers.spec.ts rewritten with subscription/invoice fixtures. - licenses.spec.ts uses `sub_*` IDs and `stripeSubscriptionId`. - remint.spec.ts already on the new naming. Follow-up (out-of-band, not in this PR) - Update the production Stripe webhook endpoint `we_1TZcsHGYRsLErhxbdN2JTFTr` `enabled_events`: add `customer.subscription.created`, `customer.subscription.updated`, `customer.subscription.deleted`, `invoice.paid`; keep `charge.refunded`; drop `checkout.session.completed`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New landing page for @ngaf/ag-ui that lives alongside /langgraph. Positioning: - Hero: "One adapter. Eight backends." — leads with the multi-backend differentiation (LangGraph / CrewAI / Mastra / MS Agent Framework / AG2 / Pydantic AI / AWS Strands / CopilotKit) - Light decision callout under the pills points LangGraph-on-LangGraph visitors to @ngaf/langgraph for the native path - FeatureBlock 1 visual: BackendsGrid listing the 8 runtimes - FeatureBlock 2 visual: BrowserFrame with provideAgUiAgent app.config.ts - Mirrors /langgraph structure (Hero → FeatureBlock × 2 → FinalCTA) Footer: - New AG-UI link in the Libraries column under LangGraph - footer_ag_ui added to CtaId union Audit cleanup folded in: - AngularPage → LangGraphPage function rename - AngularCodeShowcase → LangGraphCodeShowcase (dir + file + export) - Footer "Getting Started" (duplicate of Documentation) replaced with "Pilot to Prod" to surface that page in the footer Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nggraph rename - CompareTable.tsx: explicitly type `variant: 'primary' | 'secondary'` so the ternary doesn't widen to `string` (which fails Button's variant prop type) - e2e/website.spec.ts: replace /angular with /langgraph in canonical-URL and whitepaper-link expectations; also add /ag-ui to the canonical sweep Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ructure - 'plan cards' test asserts new tier names (Community / Developer Seat / Team / Enterprise) instead of the dropped "Open Source" label - 'lead form validates required fields' now scopes to #lead-form to avoid triggering the comparison table's Stripe checkout buttons (which are also type=submit) - 'lead form posts to /api/leads' uses the new "Request enterprise quote" CTA label (was "Get in touch") and scopes locator queries to the lead form so they don't collide with other form inputs on the page Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c68752b to
3982ba2
Compare
blove
added a commit
that referenced
this pull request
May 25, 2026
Ran scripts/stripe/sync-products.ts against the live Stripe test-mode account after PR #532 landed. The script created 4 recurring prices (monthly + annual × developer_seat + team) and archived the prior one-time-payment prices. Operational follow-ups completed in parallel: - DB migration 0002 applied to the minting Neon DB (stripe_payment_id → stripe_subscription_id) - Webhook endpoint we_1TZcsHGYRsLErhxbdN2JTFTr enabled_events updated: customer.subscription.{created,updated,deleted}, invoice.paid, charge.refunded (dropped checkout.session.completed) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Editorial cleanup of every licensing-touching surface plus a new canonical
/docs/licensinglanding page.Strategy decisions (from prior brainstorm)
THREADPLANE_LICENSEenv var) — drop "Cacheplane" naming in customer-visible copyFiles
Out of scope (deferred)
Test plan
🤖 Generated with Claude Code