Clarify Pulse Account monitored-system copy

This commit is contained in:
rcourtman 2026-04-15 10:47:58 +01:00
parent 429f12decd
commit 169c0b113a
9 changed files with 43 additions and 11 deletions

View file

@ -480,6 +480,13 @@ decode-only compatibility inputs at the storage or grant boundary. Runtime
enforcement, entitlement payload `current` usage, checkout/activation flows,
and upgrade messaging must all treat the cap as deduped top-level monitored
systems across agent, API, and Kubernetes views.
That same counted-unit contract also owns the Pulse Account monitored-system
upgrade copy. Portal shell copy, pricing explainers, and monitored-system
upgrade helper text must describe top-level monitored systems and included
child resources directly, with concrete monitored roots such as Docker hosts,
Kubernetes clusters, Proxmox nodes, standalone hosts, and TrueNAS systems,
rather than drifting back to device-style language or generic allowance-only
copy that hides what the counted unit actually is.
That same counted-unit contract now also owns prospective API-backed
admission. Proxmox/PBS/PMG config adds, TrueNAS adds, VMware inventory
previews, and equivalent updates must ask the canonical monitored-system

View file

@ -1,5 +1,5 @@
{
"source_hash": "791f928f75e431f8302b3d3c435ed49de18d60737c2d0e4c6521e4286ff6d626",
"source_hash": "6c9c1e7fc2f927d3c1862c8fee6c226a01fbbe0593e11742a9bca698963f418b",
"build_inputs": [
"package.json",
"tsconfig.json",

View file

@ -1471,7 +1471,7 @@
if (explainer) {
summaryItems.push('<div class="helper-text">' + explainer + "</div>");
}
root.innerHTML = '<div class="billing-upgrade-root">' + summaryItems.join("") + renderUpgradePlansHTML(billingState) + (pricingState.status === "ready" && pricingState.data && pricingState.data.description ? '<div class="helper-text">' + escapeText(pricingState.data.description) + "</div>" : "") + '<div class="helper-text">' + (featureKey === "max_monitored_systems" ? "Pulse Account compares self-hosted tiers and sends completed monitored-system upgrades straight back to Pulse Pro billing." : "Pulse Account compares self-hosted tiers and sends completed checkout straight back to Pulse Pro billing.") + "</div></div>";
root.innerHTML = '<div class="billing-upgrade-root">' + summaryItems.join("") + renderUpgradePlansHTML(billingState) + (pricingState.status === "ready" && pricingState.data && pricingState.data.description ? '<div class="helper-text">' + escapeText(pricingState.data.description) + "</div>" : "") + '<div class="helper-text">' + (featureKey === "max_monitored_systems" ? "Pulse Account compares self-hosted tiers by top-level monitored systems, keeps child resources underneath those roots included, and sends completed checkout straight back to Pulse Pro billing." : "Pulse Account compares self-hosted tiers and sends completed checkout straight back to Pulse Pro billing.") + "</div></div>";
}
function renderButton(id, disabled, label) {
if (!id || !label) return;
@ -2244,10 +2244,10 @@
return normalizeUpgradeFeatureKey(featureKey) === "max_monitored_systems" ? "Upgrade monitored-system cap" : "Upgrade self-hosted plan";
}
function selfHostedUpgradeActionDescription(featureKey) {
return normalizeUpgradeFeatureKey(featureKey) === "max_monitored_systems" ? "Compare self-hosted plans and choose a higher monitored-system allowance." : "Compare self-hosted plans and continue into the commercial checkout path.";
return normalizeUpgradeFeatureKey(featureKey) === "max_monitored_systems" ? "Compare self-hosted plans by top-level monitored systems such as Docker hosts, Kubernetes clusters, Proxmox nodes, standalone hosts, and TrueNAS systems." : "Compare self-hosted plans and continue into the commercial checkout path.";
}
function selfHostedUpgradeActionHighlights(featureKey) {
return normalizeUpgradeFeatureKey(featureKey) === "max_monitored_systems" ? ["Monitored-system cap", "Plan comparison"] : ["Plan comparison", "Checkout handoff"];
return normalizeUpgradeFeatureKey(featureKey) === "max_monitored_systems" ? ["Top-level monitored systems", "Plan comparison"] : ["Plan comparison", "Checkout handoff"];
}
function renderSelfHostedUpgradeActionRow(context) {
var featureKey = normalizeUpgradeFeatureKey(context.billingState.upgradeFeatureKey);
@ -2263,7 +2263,7 @@
}
function renderSelfHostedUpgradeBillingPanel(context) {
var featureKey = normalizeUpgradeFeatureKey(context.billingState.upgradeFeatureKey);
var helperCopy = featureKey === "max_monitored_systems" ? "Choose the self-hosted tier that matches the monitored-system allowance you need. Pulse Account will send completed checkout directly back to Pulse Pro billing." : "Choose the self-hosted tier that fits this upgrade. Pulse Account will send completed checkout directly back to Pulse Pro billing.";
var helperCopy = featureKey === "max_monitored_systems" ? "Choose the self-hosted tier that fits the top-level monitored systems you run. Child resources like VMs, containers, pods, disks, backups, and services underneath those roots are included. Pulse Account will send completed checkout directly back to Pulse Pro billing." : "Choose the self-hosted tier that fits this upgrade. Pulse Account will send completed checkout directly back to Pulse Pro billing.";
return renderBillingTaskPanel(
selfHostedUpgradeActionTitle(featureKey),
"Pulse Account owns self-hosted plan selection and checkout for Pulse Pro upgrades.",

View file

@ -483,7 +483,8 @@ function routeCommercialAPI(request, response, url, scenario) {
sendJSON(response, 200, {
title: 'Simple self-hosted pricing for Pulse',
description: 'Preview pricing contract',
explainer: 'Pulse counts <strong>monitored systems</strong>, not every child resource.',
explainer:
'Pulse counts <strong>top-level monitored systems</strong>. Child resources underneath them are included.',
plans: [
{
badge: 'Recommended',

View file

@ -239,7 +239,8 @@ describe('services view', function() {
billingState.upgradePricing.data = {
title: 'Pricing',
description: 'Canonical pricing model',
explainer: 'Pulse counts <strong>monitored systems</strong>.',
explainer:
'Pulse counts <strong>top-level monitored systems</strong>. Child resources underneath them are included.',
plans: [
{
tierKicker: 'Pro+',
@ -268,6 +269,12 @@ describe('services view', function() {
expect(document.getElementById('upgrade-billing-root')?.innerHTML).toContain(
'Pulse Account will return completed checkout directly to Pulse Pro billing.',
);
expect(document.getElementById('upgrade-billing-root')?.innerHTML).toContain(
'Pulse counts <strong>top-level monitored systems</strong>. Child resources underneath them are included.',
);
expect(document.getElementById('upgrade-billing-root')?.innerHTML).toContain(
'Pulse Account compares self-hosted tiers by top-level monitored systems, keeps child resources underneath those roots included, and sends completed checkout straight back to Pulse Pro billing.',
);
expect(document.getElementById('upgrade-billing-root')?.innerHTML).not.toContain('Activate in Pulse Pro');
expect(document.getElementById('upgrade-billing-root')?.innerHTML).not.toContain('ppk_live_preview');
expect(

View file

@ -196,7 +196,7 @@ export function renderUpgradePanel(billingState: PortalBillingState, _bootstrap:
: '') +
'<div class="helper-text">' +
(featureKey === 'max_monitored_systems'
? 'Pulse Account compares self-hosted tiers and sends completed monitored-system upgrades straight back to Pulse Pro billing.'
? 'Pulse Account compares self-hosted tiers by top-level monitored systems, keeps child resources underneath those roots included, and sends completed checkout straight back to Pulse Pro billing.'
: 'Pulse Account compares self-hosted tiers and sends completed checkout straight back to Pulse Pro billing.') +
'</div>' +
'</div>';

View file

@ -637,7 +637,14 @@ describe('shell view', function() {
expect(html).toContain('Pulse Account owns the commercial handoff for self-hosted upgrades from the app.');
expect(html).toContain('Upgrade monitored-system cap');
expect(html).toContain(
'Compare self-hosted plans by top-level monitored systems such as Docker hosts, Kubernetes clusters, Proxmox nodes, standalone hosts, and TrueNAS systems.',
);
expect(html).toContain('Top-level monitored systems');
expect(html).toContain('id="upgrade-billing-root"');
expect(html).toContain(
'Choose the self-hosted tier that fits the top-level monitored systems you run. Child resources like VMs, containers, pods, disks, backups, and services underneath those roots are included. Pulse Account will send completed checkout directly back to Pulse Pro billing.',
);
expect(html).toContain('Pulse Account owns self-hosted plan selection and checkout for Pulse Pro upgrades.');
expect(html).not.toContain('id="open-manage-billing"');
expect(html).not.toContain('id="open-retrieve-billing"');

View file

@ -142,13 +142,13 @@ function selfHostedUpgradeActionTitle(featureKey: string): string {
function selfHostedUpgradeActionDescription(featureKey: string): string {
return normalizeUpgradeFeatureKey(featureKey) === 'max_monitored_systems'
? 'Compare self-hosted plans and choose a higher monitored-system allowance.'
? 'Compare self-hosted plans by top-level monitored systems such as Docker hosts, Kubernetes clusters, Proxmox nodes, standalone hosts, and TrueNAS systems.'
: 'Compare self-hosted plans and continue into the commercial checkout path.';
}
function selfHostedUpgradeActionHighlights(featureKey: string): string[] {
return normalizeUpgradeFeatureKey(featureKey) === 'max_monitored_systems'
? ['Monitored-system cap', 'Plan comparison']
? ['Top-level monitored systems', 'Plan comparison']
: ['Plan comparison', 'Checkout handoff'];
}
@ -168,7 +168,7 @@ function renderSelfHostedUpgradeActionRow(context: ShellViewContext): string {
function renderSelfHostedUpgradeBillingPanel(context: ShellViewContext): string {
var featureKey = normalizeUpgradeFeatureKey(context.billingState.upgradeFeatureKey);
var helperCopy = featureKey === 'max_monitored_systems'
? 'Choose the self-hosted tier that matches the monitored-system allowance you need. Pulse Account will send completed checkout directly back to Pulse Pro billing.'
? 'Choose the self-hosted tier that fits the top-level monitored systems you run. Child resources like VMs, containers, pods, disks, backups, and services underneath those roots are included. Pulse Account will send completed checkout directly back to Pulse Pro billing.'
: 'Choose the self-hosted tier that fits this upgrade. Pulse Account will send completed checkout directly back to Pulse Pro billing.';
return renderBillingTaskPanel(
selfHostedUpgradeActionTitle(featureKey),

View file

@ -78,6 +78,16 @@ test.describe("Pulse Account upgrade bootstrap", () => {
"Pulse Account will return completed checkout directly to Pulse Pro billing.",
),
).toBeVisible();
await expect(
page.getByText(
"Pulse counts top-level monitored systems. Child resources underneath them are included.",
),
).toBeVisible();
await expect(
page.getByText(
"Pulse Account compares self-hosted tiers by top-level monitored systems, keeps child resources underneath those roots included, and sends completed checkout straight back to Pulse Pro billing.",
),
).toBeVisible();
await expect(
page.getByText(
"Open this upgrade from Pulse Pro billing so Pulse Account can verify the secure upgrade handoff before checkout.",