Mirror launch blockers across protected HyperTwist surfaces
This commit is contained in:
parent
6da96771ad
commit
fa71d0728f
8 changed files with 161 additions and 46 deletions
|
|
@ -2259,6 +2259,10 @@ Latest same-family responsive public-route expansion follow-up on `2026-06-29`:
|
|||
blockers coming back from auth-health and billing/runtime posture, so the
|
||||
public site no longer compresses that state down to generic missing-label
|
||||
prose alone
|
||||
- the same concrete live-blocker detail now also appears inside the protected
|
||||
dashboard launch-readiness panel and the signed-in `/app/launch-status`
|
||||
route, so operator follow-through does not depend on cross-reading the
|
||||
public page or the readiness CLI to understand the remaining preview gap
|
||||
- current truthful reading after this continuation:
|
||||
- no fresh browser/public/product regression surfaced under the stronger
|
||||
responsive and live-preview proof
|
||||
|
|
|
|||
|
|
@ -392,6 +392,9 @@ Latest responsive proof and live preview-readiness revalidation later on
|
|||
- the public launch-status surface now also names the concrete live preview
|
||||
blockers from auth-health and billing/runtime posture instead of reducing
|
||||
the whole state to generic missing-label prose
|
||||
- the protected dashboard and protected `/app/launch-status` route now mirror
|
||||
that same concrete blocker detail too, so signed-in operators no longer have
|
||||
to infer the real preview gap only from the public page or the readiness CLI
|
||||
- current truthful reading after that rerun:
|
||||
- the public and protected browser surfaces now have fresh practical
|
||||
mobile/tablet proof rather than only desktop/unit confidence
|
||||
|
|
|
|||
|
|
@ -195,6 +195,13 @@ describe('DashboardOverviewPage', () => {
|
|||
expect(screen.getByText('Billing price-plan map: missing')).toBeTruthy()
|
||||
expect(screen.getByText('Paddle webhook secret: missing')).toBeTruthy()
|
||||
expect(screen.getByText('Public auth runtime posture: local-or-mixed')).toBeTruthy()
|
||||
expect(screen.getByText('Current preview blockers from the live auth runtime')).toBeTruthy()
|
||||
expect(screen.getByText('Shared browser-auth runtime is not yet production-ready on the live deployment.')).toBeTruthy()
|
||||
expect(screen.getByText('Paddle webhook secret is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Billing product-plan map is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Billing price-plan map is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Runtime error: API_DOMAIN still points at localhost')).toBeTruthy()
|
||||
expect(screen.getByText('Runtime warning: COOKIE_SECURE disabled')).toBeTruthy()
|
||||
expect(screen.getByText('Current release action')).toBeTruthy()
|
||||
expect(screen.getByText('Desktop access is visible, but no packaged target is published yet.')).toBeTruthy()
|
||||
expect(screen.getAllByRole('link', { name: 'Review release notes' })[0]?.getAttribute('href')).toBe('/changelog')
|
||||
|
|
|
|||
|
|
@ -300,6 +300,84 @@ describe('protected app pages', () => {
|
|||
expect(screen.getByText(/Public launch is not fully configured yet: preview-tier launch gate\./i)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('surfaces concrete live preview blockers on the protected launch route', async () => {
|
||||
mockGetAuthHealth.mockResolvedValue({
|
||||
ok: true,
|
||||
service: 'hypertwist-auth-server',
|
||||
supertokens: {
|
||||
configured: true,
|
||||
reachable: true,
|
||||
ready: false,
|
||||
apiVersion: '5.0',
|
||||
oauth: {
|
||||
github: false,
|
||||
google: false,
|
||||
},
|
||||
},
|
||||
fallback: {
|
||||
enabled: true,
|
||||
active: false,
|
||||
reason: null,
|
||||
},
|
||||
billing: {
|
||||
statePath: '/tmp/hypertwist-billing.json',
|
||||
processedEventCount: 1,
|
||||
pricePlanMapConfigured: false,
|
||||
productPlanMapConfigured: false,
|
||||
webhookSecretConfigured: false,
|
||||
},
|
||||
runtime: {
|
||||
mode: 'mixed',
|
||||
public_origin_ready: true,
|
||||
cookie_secure: true,
|
||||
api_domain: 'https://hypertwist.app',
|
||||
website_domain: 'https://hypertwist.app',
|
||||
warnings: ['SUPERTOKENS_CORE_URI still targets a loopback/local-development host.'],
|
||||
errors: [],
|
||||
},
|
||||
launch: {
|
||||
posture: 'preview',
|
||||
ready: false,
|
||||
readiness: {
|
||||
windowsDownloadConfigured: false,
|
||||
operatorCheckoutConfigured: false,
|
||||
studioCheckoutConfigured: false,
|
||||
mplSourceConfigured: true,
|
||||
openSourceRepoConfigured: true,
|
||||
billingProductPlanMapConfigured: false,
|
||||
billingPricePlanMapConfigured: false,
|
||||
billingWebhookSecretConfigured: false,
|
||||
publicAuthRuntimeReady: false,
|
||||
},
|
||||
checklist: [
|
||||
{
|
||||
id: 'windows-download',
|
||||
label: 'Windows release authority',
|
||||
configured: false,
|
||||
requiredForPublicLaunch: true,
|
||||
},
|
||||
],
|
||||
missingLabels: ['windows release authority', 'operator checkout URL', 'studio checkout URL'],
|
||||
targets: {
|
||||
operatorCheckoutTarget: null,
|
||||
studioCheckoutTarget: null,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
renderPage(<ProtectedLaunchStatusPage />, '/app/launch-status')
|
||||
|
||||
expect(await screen.findByText('Current preview blockers from the live auth runtime')).toBeTruthy()
|
||||
expect(screen.getByText('Windows release lane is not configured on the live preview deployment yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Operator checkout is still on support fallback in the live preview deployment.')).toBeTruthy()
|
||||
expect(screen.getByText('Studio checkout is still on support fallback in the live preview deployment.')).toBeTruthy()
|
||||
expect(screen.getByText('Shared browser-auth runtime is not yet production-ready on the live deployment.')).toBeTruthy()
|
||||
expect(screen.getByText('Paddle webhook secret is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Billing product-plan map is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Billing price-plan map is not configured yet.')).toBeTruthy()
|
||||
expect(screen.getByText('Runtime warning: SUPERTOKENS_CORE_URI still targets a loopback/local-development host.')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('keeps the protected download center aligned with packaged proof, notices, and escalation guidance', async () => {
|
||||
renderPage(<DownloadCenterPage />, '/app/downloads?platform=windows')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,53 +1,11 @@
|
|||
import { useMemo } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { getAuthHealth, type AuthHealthPayload } from '../../auth/auth-api'
|
||||
import { resolvePublicLaunchStatusSummary } from '../../public-launch'
|
||||
import { getAuthHealth } from '../../auth/auth-api'
|
||||
import { buildLiveLaunchBlockerDetails, resolvePublicLaunchStatusSummary } from '../../public-launch'
|
||||
import { launchReadiness } from '../../site-config'
|
||||
import { buildProtectedDownloadPath } from '../../site-routes'
|
||||
|
||||
function buildLiveLaunchBlockerDetails(
|
||||
authHealth: AuthHealthPayload | undefined,
|
||||
launchStatus: ReturnType<typeof resolvePublicLaunchStatusSummary>,
|
||||
) {
|
||||
if (!authHealth) {
|
||||
return []
|
||||
}
|
||||
|
||||
const details = new Set<string>()
|
||||
|
||||
if (!launchStatus.readiness.windowsDownloadConfigured) {
|
||||
details.add('Windows release lane is not configured on the live preview deployment yet.')
|
||||
}
|
||||
if (!launchStatus.readiness.operatorCheckoutConfigured) {
|
||||
details.add('Operator checkout is still on support fallback in the live preview deployment.')
|
||||
}
|
||||
if (!launchStatus.readiness.studioCheckoutConfigured) {
|
||||
details.add('Studio checkout is still on support fallback in the live preview deployment.')
|
||||
}
|
||||
if (!launchStatus.readiness.publicAuthRuntimeReady) {
|
||||
details.add('Shared browser-auth runtime is not yet production-ready on the live deployment.')
|
||||
}
|
||||
if (!authHealth.billing.webhookSecretConfigured) {
|
||||
details.add('Paddle webhook secret is not configured yet.')
|
||||
}
|
||||
if (!authHealth.billing.productPlanMapConfigured) {
|
||||
details.add('Billing product-plan map is not configured yet.')
|
||||
}
|
||||
if (!authHealth.billing.pricePlanMapConfigured) {
|
||||
details.add('Billing price-plan map is not configured yet.')
|
||||
}
|
||||
|
||||
authHealth.runtime.errors.forEach((item) => {
|
||||
details.add(`Runtime error: ${item}`)
|
||||
})
|
||||
authHealth.runtime.warnings.forEach((item) => {
|
||||
details.add(`Runtime warning: ${item}`)
|
||||
})
|
||||
|
||||
return Array.from(details)
|
||||
}
|
||||
|
||||
function usePublicLaunchStatus() {
|
||||
const authHealthQuery = useQuery({
|
||||
queryKey: ['auth-health', 'public-launch-status'],
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useEffect, useMemo } from 'react'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { Link, useSearchParams } from 'react-router-dom'
|
||||
import { buildAuthApiBaseUrls, createDesktopLinkToken, getAuthHealth, getReleaseManifest } from '../auth/auth-api'
|
||||
import { buildAuthApiBaseUrls, createDesktopLinkToken, getAuthHealth, getReleaseManifest, type AuthHealthPayload } from '../auth/auth-api'
|
||||
import { usePlatformAuth, type PlatformUser } from '../auth/platform-auth'
|
||||
import { SiteMetadata } from '../components/seo/SiteMetadata'
|
||||
import { OperationalStatusCallout } from '../components/ui/OperationalStatusCallout'
|
||||
import { ProductSurfaceMatrix } from '../components/ui/ProductSurfaceMatrix'
|
||||
import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary'
|
||||
import { resolvePublicLaunchStatusSummary } from '../public-launch'
|
||||
import { buildLiveLaunchBlockerDetails, resolvePublicLaunchStatusSummary } from '../public-launch'
|
||||
import { downloadTargets, launchReadiness, mplSourceUrl, openSourceRepoUrl, publicDocsUrl, releaseNotesUrl } from '../site-config'
|
||||
import { buildReleaseMetadataItems, resolveReleaseManifestView, type ReleaseManifestView } from '../release-manifest'
|
||||
import {
|
||||
|
|
@ -451,6 +451,7 @@ function ProtectedLaunchReadinessPanel({
|
|||
title,
|
||||
kicker,
|
||||
description,
|
||||
authHealth,
|
||||
launchStatus,
|
||||
releaseManifestQuery,
|
||||
actions = [],
|
||||
|
|
@ -458,6 +459,7 @@ function ProtectedLaunchReadinessPanel({
|
|||
title: string
|
||||
kicker: string
|
||||
description?: string
|
||||
authHealth?: AuthHealthPayload | null
|
||||
launchStatus: ReturnType<typeof resolvePublicLaunchStatusSummary>
|
||||
releaseManifestQuery: {
|
||||
isLoading: boolean
|
||||
|
|
@ -466,6 +468,10 @@ function ProtectedLaunchReadinessPanel({
|
|||
actions?: readonly ProtectedLaunchReadinessAction[]
|
||||
}) {
|
||||
const launchReadinessIssues = launchStatus.missingLabels
|
||||
const liveBlockerDetails = useMemo(
|
||||
() => buildLiveLaunchBlockerDetails(authHealth, launchStatus),
|
||||
[authHealth, launchStatus],
|
||||
)
|
||||
|
||||
return (
|
||||
<Panel title={title} kicker={kicker}>
|
||||
|
|
@ -484,6 +490,16 @@ function ProtectedLaunchReadinessPanel({
|
|||
<li>Operator checkout target: {launchStatus.targets.operatorCheckoutTarget || 'support fallback active'}</li>
|
||||
<li>Studio checkout target: {launchStatus.targets.studioCheckoutTarget || 'support fallback active'}</li>
|
||||
</ul>
|
||||
{liveBlockerDetails.length > 0 ? (
|
||||
<div className="callout top-gap">
|
||||
<p>{launchStatus.ready ? 'Live runtime notes' : 'Current preview blockers from the live auth runtime'}</p>
|
||||
<ul className="list">
|
||||
{liveBlockerDetails.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : null}
|
||||
{releaseManifestQuery.isLoading ? (
|
||||
<p>Refreshing release-manifest download readiness from the auth server.</p>
|
||||
) : null}
|
||||
|
|
@ -1316,6 +1332,7 @@ export function DashboardOverviewPage() {
|
|||
<ProtectedLaunchReadinessPanel
|
||||
title="Launch readiness"
|
||||
kicker="Public release configuration"
|
||||
authHealth={healthQuery.data}
|
||||
launchStatus={launchStatus}
|
||||
releaseManifestQuery={releaseManifestQuery}
|
||||
actions={[
|
||||
|
|
@ -1416,6 +1433,7 @@ export function ProtectedLaunchStatusPage() {
|
|||
title="Protected launch status"
|
||||
kicker="Signed-in rollout authority"
|
||||
description="This signed-in route keeps the live launch checklist, current targets, and rollout blockers in one operator-facing surface instead of leaving launch authority distributed across the dashboard and public pages."
|
||||
authHealth={healthQuery.data}
|
||||
launchStatus={launchStatus}
|
||||
releaseManifestQuery={releaseManifestQuery}
|
||||
actions={[
|
||||
|
|
|
|||
|
|
@ -83,6 +83,48 @@ export function resolvePublicLaunchStatusSummary(
|
|||
})
|
||||
}
|
||||
|
||||
export function buildLiveLaunchBlockerDetails(
|
||||
authHealth: AuthHealthPayload | null | undefined,
|
||||
launchStatus: PublicLaunchStatusSummary,
|
||||
) {
|
||||
if (!authHealth) {
|
||||
return []
|
||||
}
|
||||
|
||||
const details = new Set<string>()
|
||||
|
||||
if (!launchStatus.readiness.windowsDownloadConfigured) {
|
||||
details.add('Windows release lane is not configured on the live preview deployment yet.')
|
||||
}
|
||||
if (!launchStatus.readiness.operatorCheckoutConfigured) {
|
||||
details.add('Operator checkout is still on support fallback in the live preview deployment.')
|
||||
}
|
||||
if (!launchStatus.readiness.studioCheckoutConfigured) {
|
||||
details.add('Studio checkout is still on support fallback in the live preview deployment.')
|
||||
}
|
||||
if (!launchStatus.readiness.publicAuthRuntimeReady) {
|
||||
details.add('Shared browser-auth runtime is not yet production-ready on the live deployment.')
|
||||
}
|
||||
if (!authHealth.billing.webhookSecretConfigured) {
|
||||
details.add('Paddle webhook secret is not configured yet.')
|
||||
}
|
||||
if (!authHealth.billing.productPlanMapConfigured) {
|
||||
details.add('Billing product-plan map is not configured yet.')
|
||||
}
|
||||
if (!authHealth.billing.pricePlanMapConfigured) {
|
||||
details.add('Billing price-plan map is not configured yet.')
|
||||
}
|
||||
|
||||
authHealth.runtime.errors.forEach((item) => {
|
||||
details.add(`Runtime error: ${item}`)
|
||||
})
|
||||
authHealth.runtime.warnings.forEach((item) => {
|
||||
details.add(`Runtime warning: ${item}`)
|
||||
})
|
||||
|
||||
return Array.from(details)
|
||||
}
|
||||
|
||||
export {
|
||||
buildPublicLaunchStatusSummary,
|
||||
normalizePublicLaunchReadiness,
|
||||
|
|
|
|||
|
|
@ -1036,6 +1036,11 @@ export const publicManualRouteAtlasCards = [
|
|||
] as const
|
||||
|
||||
export const changelogEntries = [
|
||||
{
|
||||
date: 'June 29, 2026',
|
||||
title: 'Protected launch surfaces now name the concrete live preview blockers too',
|
||||
details: 'The signed-in dashboard and protected `/app/launch-status` lane now mirror the same concrete live blocker details already shown on the public launch surface. Instead of only generic missing-label prose, signed-in operators now see whether Windows release publication, operator/studio checkout, webhook secret, billing plan maps, shared-auth runtime readiness, or live runtime warnings are the actual remaining preview blockers.',
|
||||
},
|
||||
{
|
||||
date: 'June 29, 2026',
|
||||
title: 'Responsive route proof and live preview-readiness were revalidated again',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue