Rename retired trial acquisition proof assets

This commit is contained in:
rcourtman 2026-04-28 18:38:10 +01:00
parent 2d5373ac8d
commit 99e65d7e68
16 changed files with 44 additions and 59 deletions

View file

@ -1,4 +1,4 @@
# Trial-Retirement + Cloud E2E on Proxmox LXC (Snapshot-Clean)
# Retired Trial Acquisition + Cloud E2E on Proxmox LXC (Snapshot-Clean)
This runbook defines a clean, repeatable validation loop for retired self-hosted trial acquisition and Pulse Cloud signup behavior on a Pulse binary running in Proxmox LXC.
@ -50,7 +50,7 @@ pct start <ctid>
Create a baseline snapshot once the container is in known-good state:
```bash
pct snapshot <ctid> pre-eval-baseline --description "Pulse trial e2e baseline"
pct snapshot <ctid> pre-eval-baseline --description "Pulse retired trial acquisition e2e baseline"
pct listsnapshot <ctid>
```
@ -72,7 +72,7 @@ pct exec <ctid> -- sh -lc 'ss -lntp | grep -E ":(7655|8443)\\b"'
Use:
- `tests/integration/scripts/trial-signup-contract.sh`
- `tests/integration/scripts/retired-trial-acquisition-contract.sh`
This script asserts:
@ -85,8 +85,8 @@ This script asserts:
Run inside container:
```bash
pct push <ctid> tests/integration/scripts/trial-signup-contract.sh /tmp/trial-signup-contract.sh
pct exec <ctid> -- sh -lc 'chmod +x /tmp/trial-signup-contract.sh && PULSE_E2E_USERNAME=admin PULSE_E2E_PASSWORD=adminadminadmin /tmp/trial-signup-contract.sh'
pct push <ctid> tests/integration/scripts/retired-trial-acquisition-contract.sh /tmp/retired-trial-acquisition-contract.sh
pct exec <ctid> -- sh -lc 'chmod +x /tmp/retired-trial-acquisition-contract.sh && PULSE_E2E_USERNAME=admin PULSE_E2E_PASSWORD=adminadminadmin /tmp/retired-trial-acquisition-contract.sh'
```
## Clean Eval Loop (Rollback -> Run)
@ -96,11 +96,11 @@ Use this loop for repeatable runs:
```bash
for i in 1 2 3; do
pct rollback <ctid> pre-eval-baseline --start 1
pct exec <ctid> -- sh -lc 'PULSE_E2E_USERNAME=admin PULSE_E2E_PASSWORD=adminadminadmin /tmp/trial-signup-contract.sh'
pct exec <ctid> -- sh -lc 'PULSE_E2E_USERNAME=admin PULSE_E2E_PASSWORD=adminadminadmin /tmp/retired-trial-acquisition-contract.sh'
done
```
If each run prints `PASS: self-hosted trial-start route is retired`, state pollution between runs is eliminated.
If each run prints `PASS: self-hosted trial acquisition routes are retired`, state pollution between runs is eliminated.
On a fresh rollback the probe should show `trial_start_code=404` and matching entitlement summaries before and after the route probe.
## Full Sandbox E2E (Playwright)

View file

@ -44,7 +44,7 @@ Companion drill:
`go test ./internal/cloudcp/... -count=1`
`go test ./internal/hosted/... -count=1`
`cd frontend-modern && npx vitest run src/pages/__tests__/HostedSignup.test.tsx src/components/Settings/__tests__/BillingAdminPanel.test.tsx`
`cd tests/integration && PULSE_E2E_USE_LOCAL_BACKEND=1 PULSE_E2E_SKIP_PLAYWRIGHT_INSTALL=1 npm test -- tests/07-trial-signup-return.spec.ts --project=chromium`
`cd tests/integration && PULSE_E2E_USE_LOCAL_BACKEND=1 PULSE_E2E_SKIP_PLAYWRIGHT_INSTALL=1 npm test -- tests/07-retired-trial-acquisition.spec.ts --project=chromium`
`cd tests/integration && PULSE_E2E_USE_LOCAL_BACKEND=1 PULSE_E2E_SKIP_PLAYWRIGHT_INSTALL=1 npm test -- tests/58-self-hosted-trial-rate-limit-ui.spec.ts --project=chromium`
The self-hosted proof must keep the retired trial-start transport contract:
`POST /api/license/trial/start` returns `404` on an ordinary self-hosted v6

View file

@ -28,7 +28,7 @@
- `code=trial_signup_required`
- `details.action_url` pointed at the hosted checkout origin with `org_id=default` and `return_url=http://127.0.0.1:17765/auth/trial-activate`
- `GET /api/license/entitlements` stayed `tier=free` and `subscription_state=expired` before and after the redirect handoff
- this live hosted replay exercised only the canonical handoff branch; the later `429 trial_rate_limited` plus `Retry-After` limiter branch stays covered by the owned local trial-start contract proof in `tests/integration/scripts/trial-signup-contract.sh` and `tests/integration/tests/07-trial-signup-return.spec.ts`
- this live hosted replay exercised only the canonical handoff branch; the later retired-route contract stays covered by the owned local trial-start proof in `tests/integration/scripts/retired-trial-acquisition-contract.sh` and `tests/integration/tests/07-retired-trial-acquisition.spec.ts`
3. Exercised real hosted signup on the live HTTP surface via `POST /api/public/signup` and confirmed:
- `201 Created`
- returned `org_id=0147dd50-38db-4316-8d46-0e1f0d754bf6`

View file

@ -1005,12 +1005,12 @@ The matching control-plane acquisition routes are retired too:
unregistered. `/api/entitlements/refresh` remains the only hosted entitlement
refresh endpoint for already-issued hosted leases.
Browser and shell coverage now guard that retired boundary:
`tests/integration/tests/07-trial-signup-return.spec.ts` and
`tests/integration/scripts/trial-signup-contract.sh` must expect `404` from the
`tests/integration/tests/07-retired-trial-acquisition.spec.ts` and
`tests/integration/scripts/retired-trial-acquisition-contract.sh` must expect `404` from the
retired route and prove entitlements remain unchanged. The paid-prompt browser
proof in `tests/integration/tests/58-self-hosted-trial-rate-limit-ui.spec.ts`
must keep trial CTAs and paid-only navigation out of the default self-hosted UI.
`scripts/tests/test-trial-signup-docs.sh` guards the same documentation posture
`scripts/tests/test-retired-trial-acquisition-docs.sh` guards the same documentation posture
so active operator docs and eval metadata describe the retired route instead of
the old hosted-signup acquisition contract.
Hosted tenant organization seeding and hosted handoff role mapping now belong

View file

@ -847,7 +847,7 @@ traffic, so split browser/backend proof can target fresh frontend code without
rewiring the API-side contract.
That same integration-README ownership includes the retired local commercial
trial probe guidance. The snapshot-clean trial instructions for
`tests/integration/scripts/trial-signup-contract.sh` must describe
`tests/integration/scripts/retired-trial-acquisition-contract.sh` must describe
`POST /api/license/trial/start` as retired in ordinary self-hosted v6 and must
expect `404` plus unchanged entitlements. The reused-instance browser-proof
entry in `tests/integration/README.md` must carry that same retired-route
@ -858,7 +858,7 @@ browser proof, `tests/58-self-hosted-trial-rate-limit-ui.spec.ts`, on the
owned paid-prompt surface so the user-facing no-trial-CTA proof does not drift
into an orphaned integration spec. The eval-pack metadata in
`tests/integration/evals/scenarios.json` must carry those same anchors for the
`trial-signup` scenario description, so deterministic and agentic trial runs
`retired-trial-acquisition` scenario description, so deterministic and agentic trial runs
inherit the same canonical contract wording instead of teaching a drifted
summary path.
Playwright-driven public/commercial specs that support scenario-specific

View file

@ -86,7 +86,7 @@ Pulse is available in three self-hosted tiers plus hosted Cloud:
- **Community**: Free self-hosted monitoring with unlimited core monitoring and 7-day history.
- **Relay**: Adds remote access, mobile, push notifications, and 14-day history.
- **Pro**: Adds AI investigation, auto-fix, operations tooling, governance features, and 90-day history.
- **Pro**: Adds alert-triggered root-cause analysis, safe remediation workflows, operations tooling, governance features, and 90-day history.
- **Cloud**: Hosted Pulse with Pro-level capabilities; hosted pricing is unchanged by the self-hosted model lock.
- **[Learn more at pulserelay.pro](https://pulserelay.pro)**

View file

@ -760,7 +760,7 @@ test_integration_readme_documents_retired_trial_start_contract() {
local output
output="$(
awk '
/^### Snapshot-Clean Proxmox LXC Trial SAT$/ { capture=1 }
/^### Snapshot-Clean Proxmox LXC Retired Trial Acquisition SAT$/ { capture=1 }
capture { print }
/^### Run Theme Visual Regression Suite$/ {
exit
@ -771,7 +771,7 @@ test_integration_readme_documents_retired_trial_start_contract() {
assert_contains "integration readme names trial-start route" "${output}" "POST /api/license/trial/start"
assert_contains "integration readme documents retired route status" "${output}" "404"
assert_contains "integration readme documents unchanged entitlements" "${output}" "without mutating entitlements"
assert_contains "integration readme references retired trial probe script" "${output}" "tests/integration/scripts/trial-signup-contract.sh"
assert_contains "integration readme references retired trial probe script" "${output}" "tests/integration/scripts/retired-trial-acquisition-contract.sh"
assert_contains "integration readme references paid prompt proof" "${output}" "tests/58-self-hosted-trial-rate-limit-ui.spec.ts"
assert_contains "integration readme documents no trial CTAs" "${output}" "trial CTAs"
assert_not_contains "integration readme no longer documents hosted-signup redirect code" "${output}" "409 trial_signup_required"

View file

@ -2,10 +2,10 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
RUNBOOK="${ROOT_DIR}/docs/operations/TRIAL_E2E_LXC_SNAPSHOT_RUNBOOK.md"
RUNBOOK="${ROOT_DIR}/docs/operations/RETIRED_TRIAL_ACQUISITION_LXC_SNAPSHOT_RUNBOOK.md"
UPGRADE_DOC="${ROOT_DIR}/docs/UPGRADE_v6.md"
INTEGRATION_README="${ROOT_DIR}/tests/integration/README.md"
EVAL_TASK_DOC="${ROOT_DIR}/tests/integration/evals/tasks/trial-signup.md"
EVAL_TASK_DOC="${ROOT_DIR}/tests/integration/evals/tasks/retired-trial-acquisition.md"
EVAL_SCENARIOS_DOC="${ROOT_DIR}/tests/integration/evals/scenarios.json"
SOURCE_OF_TRUTH_DOC="${ROOT_DIR}/docs/release-control/v6/internal/SOURCE_OF_TRUTH.md"
HIGH_RISK_MATRIX_DOC="${ROOT_DIR}/docs/release-control/v6/internal/HIGH_RISK_RELEASE_VERIFICATION_MATRIX.md"
@ -104,11 +104,11 @@ main() {
done
if (( failures > 0 )); then
echo "trial-start retired docs smoke tests failed: ${failures}" >&2
echo "retired trial acquisition docs smoke tests failed: ${failures}" >&2
exit 1
fi
echo "trial-start retired docs smoke tests passed."
echo "retired trial acquisition docs smoke tests passed."
}
main "$@"

View file

@ -27,7 +27,7 @@ End-to-end Playwright tests that validate critical user flows against a running
- `tests/06-theme-visual.spec.ts` — visual regression baselines for light/dark auth surfaces:
- Logged-out login page (full page + form card)
- Authenticated Settings → Authentication page
- `tests/07-trial-signup-return.spec.ts` — retired self-hosted trial-start route contract:
- `tests/07-retired-trial-acquisition.spec.ts` — retired self-hosted trial acquisition route contract:
- Probe `POST /api/license/trial/start` and verify ordinary self-hosted v6 returns `404`
- Verify local entitlements remain unchanged after the retired route probe
- `tests/58-self-hosted-trial-rate-limit-ui.spec.ts` — paid prompt visibility contract:
@ -73,7 +73,7 @@ npm test
```
### Eval Packs (No Manual Steps)
Run the curated scenario pack (multi-tenant, trial-signup, cloud-hosting, cloud-billing-lifecycle) and emit a report:
Run the curated scenario pack (multi-tenant, retired-trial-acquisition, cloud-hosting, cloud-billing-lifecycle) and emit a report:
```bash
cd tests/integration
npm run evals
@ -81,7 +81,7 @@ npm run evals
Filter to one scenario:
```bash
npm run evals -- --scenario trial-signup
npm run evals -- --scenario retired-trial-acquisition
```
Reports are written to:
@ -188,17 +188,17 @@ The harness understands these profiles:
- `PULSE_E2E_ENTITLEMENT_PROFILE=multi-tenant` for Enterprise/MSP multi-tenant coverage.
- `PULSE_E2E_ENTITLEMENT_PROFILE=infra` for Pro/relay/reporting-style journeys.
### Snapshot-Clean Proxmox LXC Trial SAT
### Snapshot-Clean Proxmox LXC Retired Trial Acquisition SAT
For retired self-hosted trial-start validation against a fresh LXC each run:
- Runbook: `docs/operations/TRIAL_E2E_LXC_SNAPSHOT_RUNBOOK.md`
- Probe script: `tests/integration/scripts/trial-signup-contract.sh`
- Runbook: `docs/operations/RETIRED_TRIAL_ACQUISITION_LXC_SNAPSHOT_RUNBOOK.md`
- Probe script: `tests/integration/scripts/retired-trial-acquisition-contract.sh`
- Exercises `POST /api/license/trial/start` from a clean snapshot and proves
ordinary self-hosted v6 returns `404` without mutating entitlements
- Pulse Pro browser proof: `tests/58-self-hosted-trial-rate-limit-ui.spec.ts`
- Exercises `/settings/system/billing` on the real browser shell and proves
trial CTAs and paid-only navigation stay out of the default self-hosted UI
- Full sandbox orchestration (multi-tenant + trial + cloud, with per-scenario snapshot reset):
- Full sandbox orchestration (multi-tenant + retired trial acquisition + cloud, with per-scenario snapshot reset):
- `tests/integration/scripts/run-lxc-sandbox-evals.sh`
- Includes retired self-hosted trial-start validation and cloud subscription cancellation lifecycle verification

View file

@ -24,22 +24,22 @@
}
},
{
"id": "trial-signup",
"name": "Pro Trial Signup Return",
"id": "retired-trial-acquisition",
"name": "Retired Trial Acquisition",
"description": "Ensures self-hosted Pulse no longer initiates hosted Pro trial acquisition from the ordinary runtime: POST /api/license/trial/start returns 404 and preserves local entitlements.",
"deterministic": {
"command": [
"npx",
"playwright",
"test",
"tests/07-trial-signup-return.spec.ts",
"tests/07-retired-trial-acquisition.spec.ts",
"--project=chromium",
"--reporter=list",
"--retries=1"
]
},
"agentic": {
"task_file": "evals/tasks/trial-signup.md"
"task_file": "evals/tasks/retired-trial-acquisition.md"
}
},
{

View file

@ -1,4 +1,4 @@
# Scenario: Retired Pro Trial Signup
# Scenario: Retired Trial Acquisition
## Goal
Prove the self-hosted Pulse instance no longer initiates a hosted Pro trial from the ordinary in-app runtime and does not mutate local entitlements when the retired route is probed.

View file

@ -20,7 +20,7 @@ PULSE_E2E_PASSWORD="${PULSE_E2E_PASSWORD:-adminadminadmin}"
PULSE_E2E_BOOTSTRAP_TOKEN="${PULSE_E2E_BOOTSTRAP_TOKEN:-}"
MULTI_TENANT_SCENARIO="${MULTI_TENANT_SCENARIO:-multi-tenant}"
TRIAL_SCENARIO="${TRIAL_SCENARIO:-trial-signup}"
RETIRED_TRIAL_ACQUISITION_SCENARIO="${RETIRED_TRIAL_ACQUISITION_SCENARIO:-retired-trial-acquisition}"
CLOUD_SCENARIOS="${CLOUD_SCENARIOS:-cloud-hosting,cloud-billing-lifecycle}"
TRUENAS_SCENARIO="${TRUENAS_SCENARIO:-truenas-node-add}"
RELAY_SCENARIO="${RELAY_SCENARIO:-relay-pairing}"
@ -232,11 +232,11 @@ seed_multi_tenant_entitlement
wait_http_ready "http://127.0.0.1:${LOCAL_PULSE_PORT}/api/health"
run_eval_scenarios "${MULTI_TENANT_SCENARIO}"
echo "[6/${TOTAL_STEPS}] Restoring clean baseline and running ${TRIAL_SCENARIO}"
echo "[6/${TOTAL_STEPS}] Restoring clean baseline and running ${RETIRED_TRIAL_ACQUISITION_SCENARIO}"
rollback_and_start_services
wait_http_ready "http://127.0.0.1:${LOCAL_PULSE_PORT}/api/health"
wait_http_ready "http://127.0.0.1:${LOCAL_CP_PORT}/healthz"
run_eval_scenarios "${TRIAL_SCENARIO}"
run_eval_scenarios "${RETIRED_TRIAL_ACQUISITION_SCENARIO}"
echo "[7/${TOTAL_STEPS}] Restoring clean baseline for cloud lifecycle"
rollback_and_start_services

View file

@ -2,7 +2,7 @@
#
# Run Pulse integration tests with different suites
# Usage: ./run-tests.sh [suite]
# suite: all, core, diagnostic, perf, visual, multi-tenant, trial, cloud-hosting, cloud-lifecycle, demo-contract, evals, updates-api
# suite: all, core, diagnostic, perf, visual, multi-tenant, retired-trial-acquisition, cloud-hosting, cloud-lifecycle, demo-contract, evals, updates-api
#
set -e
@ -134,8 +134,8 @@ run_suite() {
multi-tenant)
npx playwright test "tests/03-multi-tenant.spec.ts" --project=chromium --reporter=list
;;
trial)
npx playwright test "tests/07-trial-signup-return.spec.ts" --project=chromium --reporter=list
retired-trial-acquisition)
npx playwright test "tests/07-retired-trial-acquisition.spec.ts" --project=chromium --reporter=list
;;
cloud-hosting)
npx playwright test "tests/08-cloud-hosting.spec.ts" --project=chromium --reporter=list
@ -188,7 +188,7 @@ case "$SUITE" in
run_suite "Diagnostic Smoke" "diagnostic" || FAILED_TESTS+=("Diagnostic Smoke")
run_suite "Core E2E" "core" || FAILED_TESTS+=("Core E2E")
run_suite "Multi-tenant E2E" "multi-tenant" "false" "false" "false" "false" "true" || FAILED_TESTS+=("Multi-tenant E2E")
run_suite "Trial Signup E2E" "trial" || FAILED_TESTS+=("Trial Signup E2E")
run_suite "Retired Trial Acquisition E2E" "retired-trial-acquisition" || FAILED_TESTS+=("Retired Trial Acquisition E2E")
run_suite "Cloud Hosting E2E" "cloud-hosting" || FAILED_TESTS+=("Cloud Hosting E2E")
run_suite "Cloud Billing Lifecycle E2E" "cloud-lifecycle" || FAILED_TESTS+=("Cloud Billing Lifecycle E2E")
run_suite "Public Demo Contract" "demo-contract" || FAILED_TESTS+=("Public Demo Contract")
@ -217,8 +217,8 @@ case "$SUITE" in
run_suite "Multi-tenant E2E" "multi-tenant" "false" "false" "false" "false" "true" || FAILED_TESTS+=("Multi-tenant E2E")
;;
trial)
run_suite "Trial Signup E2E" "trial" || FAILED_TESTS+=("Trial Signup E2E")
retired-trial-acquisition)
run_suite "Retired Trial Acquisition E2E" "retired-trial-acquisition" || FAILED_TESTS+=("Retired Trial Acquisition E2E")
;;
cloud-hosting)
@ -243,7 +243,7 @@ case "$SUITE" in
*)
echo "Unknown suite: $SUITE"
echo "Available suites: all, diagnostic, core, perf, visual, multi-tenant, trial, cloud-hosting, cloud-lifecycle, demo-contract, evals, updates-api"
echo "Available suites: all, diagnostic, core, perf, visual, multi-tenant, retired-trial-acquisition, cloud-hosting, cloud-lifecycle, demo-contract, evals, updates-api"
exit 1
;;
esac

View file

@ -10,7 +10,7 @@ type EntitlementPayload = {
is_lifetime?: boolean;
};
test.describe.serial('Retired self-hosted trial start route', () => {
test.describe.serial('Retired self-hosted trial acquisition routes', () => {
test('does not expose in-app trial acquisition and preserves local entitlements', async ({ page }, testInfo) => {
test.skip(testInfo.project.name.startsWith('mobile-'), 'Desktop-only commercial route coverage');

View file

@ -1,15 +0,0 @@
import { expect, test } from '@playwright/test';
import { apiRequest, ensureAuthenticated } from './helpers';
test.describe.serial('Retired self-hosted Pro trial sandbox path', () => {
test('does not start a hosted Stripe trial from an ordinary self-hosted runtime', async ({ page }, testInfo) => {
test.skip(testInfo.project.name.startsWith('mobile-'), 'Desktop-only commercial route coverage');
await ensureAuthenticated(page);
const startRes = await apiRequest(page, '/api/license/trial/start', {
method: 'POST',
});
expect(startRes.status(), `retired trial start route must not be exposed: HTTP ${startRes.status()}`).toBe(404);
});
});