diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md b/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md index 32e5f2b..729b8f9 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md @@ -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 diff --git a/website/README.md b/website/README.md index e5f58d6..7a00219 100644 --- a/website/README.md +++ b/website/README.md @@ -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 diff --git a/website/src/__tests__/DashboardOverviewPage.test.tsx b/website/src/__tests__/DashboardOverviewPage.test.tsx index cc178a3..ce0700c 100644 --- a/website/src/__tests__/DashboardOverviewPage.test.tsx +++ b/website/src/__tests__/DashboardOverviewPage.test.tsx @@ -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') diff --git a/website/src/__tests__/protected-app-pages.test.tsx b/website/src/__tests__/protected-app-pages.test.tsx index 1b5673e..92e2c95 100644 --- a/website/src/__tests__/protected-app-pages.test.tsx +++ b/website/src/__tests__/protected-app-pages.test.tsx @@ -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(, '/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(, '/app/downloads?platform=windows') diff --git a/website/src/components/ui/PublicLaunchStatus.tsx b/website/src/components/ui/PublicLaunchStatus.tsx index f47f16a..d2713aa 100644 --- a/website/src/components/ui/PublicLaunchStatus.tsx +++ b/website/src/components/ui/PublicLaunchStatus.tsx @@ -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, -) { - if (!authHealth) { - return [] - } - - const details = new Set() - - 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'], diff --git a/website/src/pages/app-pages.tsx b/website/src/pages/app-pages.tsx index a76bab0..ce350c8 100644 --- a/website/src/pages/app-pages.tsx +++ b/website/src/pages/app-pages.tsx @@ -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 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 ( @@ -484,6 +490,16 @@ function ProtectedLaunchReadinessPanel({
  • Operator checkout target: {launchStatus.targets.operatorCheckoutTarget || 'support fallback active'}
  • Studio checkout target: {launchStatus.targets.studioCheckoutTarget || 'support fallback active'}
  • + {liveBlockerDetails.length > 0 ? ( +
    +

    {launchStatus.ready ? 'Live runtime notes' : 'Current preview blockers from the live auth runtime'}

    +
      + {liveBlockerDetails.map((item) => ( +
    • {item}
    • + ))} +
    +
    + ) : null} {releaseManifestQuery.isLoading ? (

    Refreshing release-manifest download readiness from the auth server.

    ) : null} @@ -1316,6 +1332,7 @@ export function DashboardOverviewPage() { () + + 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, diff --git a/website/src/site-data.ts b/website/src/site-data.ts index 69f57a9..122c4ec 100644 --- a/website/src/site-data.ts +++ b/website/src/site-data.ts @@ -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',