Align in-app Plans & Billing copy with the new public pricing vocabulary

The public pricing surface (pulserelay.pro and the license-server pricing
model, pulse-pro 3d9e365/2da1290) now sells Pro with the real Patrol mode
names and plain-English job framing. The in-app plan cards still used the
old taxonomy (Choose Patrol mode, Patrol investigates issues, Patrol
handles safe fixes) and 'admin controls' phrasing. Pro plan-selling copy
now matches the public wording: mode names spelled out (Ask first, Safe
auto-fix, Autopilot), investigation explains the root cause, safe fixes
are verified, and RBAC/audit/reporting/agent profiles are 'team controls'.
Functional CTAs (the Choose Patrol mode link) and canonical mode labels
are unchanged. Test pins updated.

The Go-side feature catalog names (Patrol Investigates Issues / Patrol
Handles Safe Fixes) still carry the old phrasing and are a separate
follow-up since they regenerate from pkg/licensing.
This commit is contained in:
rcourtman
2026-07-11 01:16:00 +01:00
parent b57fb95e20
commit 0b15855de3
6 changed files with 50 additions and 46 deletions
@@ -310,12 +310,19 @@ describe('ProLicensePanel', () => {
expect(screen.getByText('Remote web access via Relay')).toBeInTheDocument();
expect(screen.getByText('Pulse Mobile pairing')).toBeInTheDocument();
expect(screen.getByText('Push notifications')).toBeInTheDocument();
expect(screen.getByText('Choose Patrol mode')).toBeInTheDocument();
expect(screen.getByText('Patrol investigates issues')).toBeInTheDocument();
expect(screen.getByText('Patrol handles safe fixes')).toBeInTheDocument();
expect(
screen.getByText('Patrol modes: Ask first, Safe auto-fix, or Autopilot'),
).toBeInTheDocument();
expect(
screen.getByText('Patrol investigates issues and explains the root cause'),
).toBeInTheDocument();
expect(
screen.getByText('Patrol applies safe fixes and verifies the result'),
).toBeInTheDocument();
expect(screen.getByText('90-day metric history')).toBeInTheDocument();
expect(screen.getByText('RBAC and audit logging')).toBeInTheDocument();
expect(screen.getByText('Agent profiles · PDF/CSV reports')).toBeInTheDocument();
expect(
screen.getByText('Team controls: RBAC, audit logging, reporting, and agent profiles'),
).toBeInTheDocument();
expect(screen.queryByText(/unlimited/i)).not.toBeInTheDocument();
expect(screen.queryByText(/trial/i)).not.toBeInTheDocument();
@@ -825,7 +832,7 @@ describe('ProLicensePanel', () => {
expect(screen.getByText('Current plan: Pulse Pro')).toBeInTheDocument();
expect(
screen.getByText(
'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
),
).toBeInTheDocument();
expect(screen.getByText('Primary capabilities')).toBeInTheDocument();
@@ -851,7 +858,7 @@ describe('ProLicensePanel', () => {
),
).toBeInTheDocument();
expect(screen.getByText('Patrol investigation and remediation')).toBeInTheDocument();
expect(screen.getByText('Team and admin controls')).toBeInTheDocument();
expect(screen.getByText('Team controls')).toBeInTheDocument();
expect(screen.getAllByText('Active').length).toBeGreaterThanOrEqual(4);
expect(
screen.queryByText(new RegExp(['optional', 'activation'].join(' '), 'i')),
@@ -863,7 +870,7 @@ describe('ProLicensePanel', () => {
expect(screen.queryByText('Needs attention')).not.toBeInTheDocument();
expect(screen.queryByText('Available plans')).not.toBeInTheDocument();
expect(screen.getByText('90 days')).toBeInTheDocument();
expect(screen.getByText('Patrol modes, history, and admin controls')).toBeInTheDocument();
expect(screen.getByText('Patrol modes, history, and team controls')).toBeInTheDocument();
expect(screen.queryByText('Guest Capacity')).not.toBeInTheDocument();
expect(screen.queryByText('Included Monitored Systems')).not.toBeInTheDocument();
expect(screen.queryByText('Monitored-system policy')).not.toBeInTheDocument();
@@ -22,7 +22,7 @@ describe('commercialBillingModel', () => {
expect(model.summary).toEqual([
{ label: 'Core Monitoring', value: 'Included' },
{ label: 'Metric History', value: '90 days' },
{ label: 'Included Extras', value: 'Patrol modes, history, and admin controls' },
{ label: 'Included Extras', value: 'Patrol modes, history, and team controls' },
]);
expect(model.details.map((item) => item.label)).toEqual([
'Tier',
@@ -339,7 +339,7 @@ describe('licensePresentation', () => {
}),
).toMatchObject({
title: 'Current plan: Pulse Pro',
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
supplementalBadges: [],
supplementalSummary: '',
});
@@ -362,7 +362,7 @@ describe('licensePresentation', () => {
}),
).toEqual({
title: 'Current plan: Pulse Pro',
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
unlockedFeaturesLabel: 'Primary capabilities',
unlockedFeatures: [
'Patrol Investigates Issues',
@@ -633,7 +633,7 @@ describe('licensePresentation', () => {
}),
).toMatchObject({
title: 'Current plan: Legacy Pulse Pro+',
body: 'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
unlockedFeaturesLabel: 'Primary capabilities',
unlockedFeatures: [
'Patrol Investigates Issues',
@@ -657,7 +657,7 @@ describe('licensePresentation', () => {
}),
).toEqual({
title: 'Current plan: Pulse Pro',
body: 'Pulse Pro is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Pulse Pro is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
unlockedFeaturesLabel: 'Primary capabilities',
unlockedFeatures: [
'Patrol Investigates Issues',
@@ -903,15 +903,14 @@ describe('licensePresentation', () => {
},
{
title: 'Pulse Pro plan',
body: 'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
highlights: [
'Everything in Relay',
'Choose Patrol mode',
'Patrol investigates issues',
'Patrol handles safe fixes',
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
'Patrol investigates issues and explains the root cause',
'Patrol applies safe fixes and verifies the result',
'90-day metric history',
'RBAC and audit logging',
'Agent profiles · PDF/CSV reports',
'Team controls: RBAC, audit logging, reporting, and agent profiles',
],
},
],
@@ -931,15 +930,14 @@ describe('licensePresentation', () => {
cards: [
{
title: 'Pulse Pro plan',
body: 'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
body: 'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
highlights: [
'Everything in Relay',
'Choose Patrol mode',
'Patrol investigates issues',
'Patrol handles safe fixes',
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
'Patrol investigates issues and explains the root cause',
'Patrol applies safe fixes and verifies the result',
'90-day metric history',
'RBAC and audit logging',
'Agent profiles · PDF/CSV reports',
'Team controls: RBAC, audit logging, reporting, and agent profiles',
],
},
],
@@ -1018,7 +1016,7 @@ describe('licensePresentation', () => {
{ label: 'Remote access, pairing, and push', statusLabel: 'Active' },
{ label: '90-day metric history', statusLabel: 'Active' },
{ label: 'Patrol investigation and remediation', statusLabel: 'Active' },
{ label: 'Team and admin controls', statusLabel: 'Active' },
{ label: 'Team controls', statusLabel: 'Active' },
],
});
@@ -1036,7 +1034,7 @@ describe('licensePresentation', () => {
['Remote access, pairing, and push', 'Partial'],
['90-day metric history', 'Partial'],
['Patrol investigation and remediation', 'Partial'],
['Team and admin controls', 'Needs attention'],
['Team controls', 'Needs attention'],
]);
expect(partialProStatus?.items).toContainEqual({
label: 'Patrol investigation and remediation',
@@ -1046,11 +1044,11 @@ describe('licensePresentation', () => {
'Some Patrol capability is available, but investigation or remediation may not be fully enabled yet.',
});
expect(partialProStatus?.items).toContainEqual({
label: 'Team and admin controls',
label: 'Team controls',
state: 'missing',
statusLabel: 'Needs attention',
detail:
'Team/admin controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
'Team controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
});
expect(partialProStatus?.items.map((item) => item.detail).join(' ')).not.toContain(
'admin tools',
@@ -1093,7 +1091,7 @@ describe('licensePresentation', () => {
['Remote access, pairing, and push', 'Active'],
['90-day metric history', 'Active'],
['Patrol investigation and remediation', 'Active'],
['Team and admin controls', 'Active'],
['Team controls', 'Active'],
];
const expectOnlyCapabilityItems = (
status?: ReturnType<typeof getSelfHostedPlanStatusPresentation>,
@@ -126,13 +126,13 @@ describe('selfHostedPlans', () => {
/yourlab\.pulserelay\.pro|custom\s+(?:url|subdomain|domain)/i,
);
expect(SELF_HOSTED_PLAN_BY_TIER.pro.billingExtrasSummary).toBe(
'Patrol modes, history, and admin controls',
'Patrol modes, history, and team controls',
);
expect(SELF_HOSTED_PLAN_BY_TIER.pro.entitlementSummary).toContain(
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
'Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
);
expect(SELF_HOSTED_PLAN_BY_TIER.pro.comparisonSummary).toBe(
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
);
expect(SELF_HOSTED_PLAN_BY_TIER.pro.comparisonSummary).not.toContain('how much control');
expect(SELF_HOSTED_PLAN_BY_TIER.pro.entitlementHighlights).toEqual([
@@ -172,7 +172,7 @@ describe('selfHostedPlans', () => {
getSelfHostedPlanEntitlementSummary('pro'),
);
expect(getSelfHostedPlanEntitlementSummary('pro', 'Legacy Pulse Pro+')).toBe(
'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
'Legacy Pulse Pro+ is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.',
);
expect(SELF_HOSTED_PLAN_BY_TIER.relay.entitlementHighlights).toEqual(
@@ -216,7 +216,7 @@ describe('selfHostedPlans', () => {
historyDays: 14,
extras: 'Remote web access, pairing, and push',
},
{ tier: 'pro', historyDays: 90, extras: 'Patrol modes, history, and admin controls' },
{ tier: 'pro', historyDays: 90, extras: 'Patrol modes, history, and team controls' },
]);
expect(SELF_HOSTED_FEATURE_ROWS.find((row) => row.key === 'history')).toEqual({
key: 'history',
@@ -924,14 +924,14 @@ export const getSelfHostedPlanStatusPresentation = (
}),
buildCapabilityStatusItem({
capabilities,
label: 'Team and admin controls',
label: 'Team controls',
requiredCapabilities: ['rbac', 'audit_logging', 'advanced_reporting', 'agent_profiles'],
activeDetail:
'RBAC, audit logging, reporting, and agent profiles are available for governed operations.',
partialDetail:
'Some team/admin capabilities are available. Refresh the plan or open recovery if any admin controls stay unavailable.',
'Some team controls are available. Refresh the plan or open recovery if any of them stay unavailable.',
missingDetail:
'Team/admin controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
'Team controls are not available yet. Refresh the plan or open recovery before relying on this Pro install.',
}),
);
}
+7 -8
View File
@@ -84,7 +84,7 @@ export function getSelfHostedPlanEntitlementSummary(
case 'relay':
return `${planLabel} is active on this instance. It includes remote web access, Pulse Mobile pairing, push notifications, and 14-day metric history.`;
case 'pro':
return `${planLabel} is active on this instance. It includes Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.`;
return `${planLabel} is active on this instance. It includes Patrol modes (Ask first, Safe auto-fix, Autopilot), 90-day metric history, RBAC, audit logging, reporting, and agent profiles.`;
}
}
@@ -150,20 +150,19 @@ export const SELF_HOSTED_PLAN_DEFINITIONS: readonly SelfHostedPlanDefinition[] =
price: '$79/year',
subline: 'or $8.99/month',
metricHistoryDays: 90,
billingExtrasSummary: 'Patrol modes, history, and admin controls',
billingExtrasSummary: 'Patrol modes, history, and team controls',
entitlementSummary: getSelfHostedPlanEntitlementSummary('pro'),
entitlementHighlights: getTierEntitlementHighlights('pro', 90),
includedExtras: getTierIncludedExtras('pro'),
comparisonSummary:
'Patrol modes, 90-day metric history, RBAC, audit logs, reports, and agent profiles.',
'Patrol investigates issues, applies safe fixes, and verifies the result, plus 90-day metric history and team controls.',
highlights: [
'Everything in Relay',
'Choose Patrol mode',
'Patrol investigates issues',
'Patrol handles safe fixes',
'Patrol modes: Ask first, Safe auto-fix, or Autopilot',
'Patrol investigates issues and explains the root cause',
'Patrol applies safe fixes and verifies the result',
'90-day metric history',
'RBAC and audit logging',
'Agent profiles · PDF/CSV reports',
'Team controls: RBAC, audit logging, reporting, and agent profiles',
],
},
] as const;