diff --git a/docs/ops/HYPERTWIST_PUBLIC_WEBSITE_CONTENT_AND_OPERATOR_MANUAL_PACKET_2026-06-22.md b/docs/ops/HYPERTWIST_PUBLIC_WEBSITE_CONTENT_AND_OPERATOR_MANUAL_PACKET_2026-06-22.md
index b70ad1f..ccd074f 100644
--- a/docs/ops/HYPERTWIST_PUBLIC_WEBSITE_CONTENT_AND_OPERATOR_MANUAL_PACKET_2026-06-22.md
+++ b/docs/ops/HYPERTWIST_PUBLIC_WEBSITE_CONTENT_AND_OPERATOR_MANUAL_PACKET_2026-06-22.md
@@ -63,6 +63,24 @@ into a more practical public operator reference:
operational language, and shipping/payment now carries the real digital
delivery workflow instead of generic storefront filler
+## Protected-surface continuation
+
+The protected browser shell is no longer only one strong dashboard route plus
+thin adjacent placeholders.
+
+The later continuation also hardened:
+
+- `/app/browser-access` with live auth-health posture, browser-to-desktop
+ handoff guidance, bounded branch truth, and release-reference links
+- `/app/account` with live release-manifest viewer posture, configured target
+ visibility, and explicit operator follow-through links
+- `/app/notices` with protected release-target notice posture, operator duties,
+ and linked protected/public notices plus corresponding-source references
+
+This keeps the protected app shell aligned with the public manual packet so the
+operator surface remains honest and useful after sign-in rather than becoming a
+drop in information quality.
+
## Public pages widened by this packet
- homepage
@@ -107,6 +125,11 @@ The same-day public-manual continuation passed:
- `npm run type-check` in `website/server`
- `npm test -- --run` in `website/server`
+The protected-surface continuation also passed:
+
+- `npm test -- --run src/__tests__/protected-app-pages.test.tsx src/__tests__/app-route-tree.test.tsx`
+- `npm test -- --run` in `website/` after the protected-route widening
+
The same validation hardening also gives the live-spawned
`website/server` bootstrap proof an explicit `15s` timeout so real child-process
boot plus same-origin HTTP verification does not fail spuriously on a loaded
diff --git a/docs/ops/HYPERTWIST_REFACTORING_TOOLCHAIN_2026-06-22.md b/docs/ops/HYPERTWIST_REFACTORING_TOOLCHAIN_2026-06-22.md
index b643fda..cc71db0 100644
--- a/docs/ops/HYPERTWIST_REFACTORING_TOOLCHAIN_2026-06-22.md
+++ b/docs/ops/HYPERTWIST_REFACTORING_TOOLCHAIN_2026-06-22.md
@@ -209,6 +209,17 @@ Follow-up tool refresh on `2026-06-22`:
is still isolated to the same Unreal validator seams rather than the browser
or website lane
+Additional follow-up after the protected website hardening continuation:
+
+- `scripts/run-hypertwist-sentrux-source-only.sh` improved slightly to
+ `Quality: 5902`
+- the remaining structural debt still stayed isolated to the same Unreal
+ validator seams:
+ - two `HyperTwistSkillTypes.h` `IsStructurallyValid()` functions
+ - one `HyperTwistRecognitionTypes.h` `IsStructurallyValid()` function
+- the protected website/account/notices/browser-access widening did not create
+ any new cycle debt or large-function debt in the browser or website lane
+
Important nuance from the `2026-06-22` follow-up:
- a helper-only readability pass on `HyperTwistSkillTypes.h` was tested and
diff --git a/website/README.md b/website/README.md
index 2abe535..cb97ac7 100644
--- a/website/README.md
+++ b/website/README.md
@@ -4,6 +4,7 @@ First-party `hypertwist.app` surface for HyperTwist:
- public homepage, about, resources, pricing, support, and legal pages
- browser-facing operator/account dashboard
+- protected browser-access, account, and notices routes backed by live auth-health and release-manifest authority
- shared SuperTokens auth posture reused from the FamiliarOS and ScriptoriumAI website lane
- desktop download posture and desktop-link handshake endpoints
- server-backed release-manifest authority shared by public and protected download surfaces
@@ -54,6 +55,9 @@ manual:
- the docs and resources pages now also carry a practical simulator-use manual
for recognition, replay, higher-dimensional runtime ownership, and operator
diagnostics without overclaiming browser or VR parity
+- the protected app shell now also carries richer operator-facing browser
+ boundary, account, entitlement, and notices guidance instead of treating
+ those routes as thin placeholders beside the main dashboard
## Local development
diff --git a/website/src/__tests__/app-route-tree.test.tsx b/website/src/__tests__/app-route-tree.test.tsx
index f322db7..12efaac 100644
--- a/website/src/__tests__/app-route-tree.test.tsx
+++ b/website/src/__tests__/app-route-tree.test.tsx
@@ -320,4 +320,97 @@ describe('AppRouteTree', () => {
})
expect(screen.getByText('Windows')).toBeTruthy()
})
+
+ it('renders the protected browser-access route through the real route tree when authenticated', async () => {
+ mockUsePlatformAuth.mockReturnValue({
+ isAuthenticated: true,
+ isLoading: false,
+ login: vi.fn(),
+ register: vi.fn(),
+ logout: vi.fn(),
+ toggleColorMode: vi.fn(),
+ colorMode: 'dark',
+ superTokensConfigured: true,
+ user: {
+ id: 'operator-1',
+ name: 'Operator',
+ email: 'operator@hypertwist.app',
+ plan: 'operator',
+ role: 'operator',
+ canDownload: true,
+ billing: {
+ source: 'session',
+ accessStatus: 'active',
+ canDownload: true,
+ lastEventType: 'transaction.completed',
+ },
+ },
+ })
+
+ renderRoute('/app/browser-access')
+ expect(await screen.findByText('Current browser posture')).toBeTruthy()
+ expect(screen.getByText('Browser-to-desktop handoff')).toBeTruthy()
+ })
+
+ it('renders the protected account route through the real route tree when authenticated', async () => {
+ mockUsePlatformAuth.mockReturnValue({
+ isAuthenticated: true,
+ isLoading: false,
+ login: vi.fn(),
+ register: vi.fn(),
+ logout: vi.fn(),
+ toggleColorMode: vi.fn(),
+ colorMode: 'dark',
+ superTokensConfigured: true,
+ user: {
+ id: 'operator-1',
+ name: 'Operator',
+ email: 'operator@hypertwist.app',
+ plan: 'operator',
+ role: 'operator',
+ canDownload: true,
+ billing: {
+ source: 'session',
+ accessStatus: 'active',
+ canDownload: true,
+ lastEventType: 'transaction.completed',
+ },
+ },
+ })
+
+ renderRoute('/app/account')
+ expect(await screen.findByText('Session profile')).toBeTruthy()
+ expect(screen.getByText('Account follow-through')).toBeTruthy()
+ })
+
+ it('renders the protected notices route through the real route tree when authenticated', async () => {
+ mockUsePlatformAuth.mockReturnValue({
+ isAuthenticated: true,
+ isLoading: false,
+ login: vi.fn(),
+ register: vi.fn(),
+ logout: vi.fn(),
+ toggleColorMode: vi.fn(),
+ colorMode: 'dark',
+ superTokensConfigured: true,
+ user: {
+ id: 'operator-1',
+ name: 'Operator',
+ email: 'operator@hypertwist.app',
+ plan: 'operator',
+ role: 'operator',
+ canDownload: true,
+ billing: {
+ source: 'session',
+ accessStatus: 'active',
+ canDownload: true,
+ lastEventType: 'transaction.completed',
+ },
+ },
+ })
+
+ renderRoute('/app/notices')
+ expect(await screen.findByText('Distribution notices')).toBeTruthy()
+ expect(screen.getByText('Reference surfaces')).toBeTruthy()
+ })
})
diff --git a/website/src/__tests__/protected-app-pages.test.tsx b/website/src/__tests__/protected-app-pages.test.tsx
new file mode 100644
index 0000000..aeee875
--- /dev/null
+++ b/website/src/__tests__/protected-app-pages.test.tsx
@@ -0,0 +1,215 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { cleanup, render, screen } from '@testing-library/react'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { MemoryRouter } from 'react-router-dom'
+
+const mockUsePlatformAuth = vi.fn()
+const mockGetAuthHealth = vi.fn()
+const mockGetReleaseManifest = vi.fn()
+
+vi.mock('../auth/platform-auth', () => ({
+ usePlatformAuth: () => mockUsePlatformAuth(),
+}))
+
+vi.mock('../auth/auth-api', () => ({
+ buildAuthApiBaseUrls: () => ['https://hypertwist.app'],
+ createDesktopLinkToken: vi.fn(),
+ getAuthHealth: (...args: unknown[]) => mockGetAuthHealth(...args),
+ getReleaseManifest: (...args: unknown[]) => mockGetReleaseManifest(...args),
+}))
+
+import { AccountPage, BrowserAccessPage, NoticesPage } from '../pages/app-pages'
+
+function renderPage(page: React.ReactNode, initialEntry: string) {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+ })
+
+ return render(
+
+
+ {page}
+
+ ,
+ )
+}
+
+function byExactTextContent(expected: string, tagName?: string) {
+ return (_content: string, node: Element | null) => (
+ node?.textContent === expected
+ && (tagName ? node.tagName === tagName.toUpperCase() : true)
+ )
+}
+
+describe('protected app pages', () => {
+ beforeEach(() => {
+ cleanup()
+ mockUsePlatformAuth.mockReset()
+ mockGetAuthHealth.mockReset()
+ mockGetReleaseManifest.mockReset()
+
+ mockUsePlatformAuth.mockReturnValue({
+ user: {
+ id: 'operator-1',
+ name: 'Operator',
+ email: 'operator@hypertwist.app',
+ authMethod: 'email',
+ plan: 'operator',
+ role: 'operator',
+ canDownload: true,
+ billing: {
+ source: 'session',
+ accessStatus: 'active',
+ canDownload: true,
+ lastEventType: 'transaction.completed',
+ },
+ },
+ superTokensConfigured: true,
+ })
+
+ mockGetAuthHealth.mockResolvedValue({
+ ok: true,
+ service: 'hypertwist-auth-server',
+ supertokens: {
+ configured: true,
+ reachable: true,
+ ready: true,
+ apiVersion: '5.0',
+ oauth: {
+ github: false,
+ google: false,
+ },
+ },
+ fallback: {
+ enabled: true,
+ active: false,
+ reason: null,
+ },
+ billing: {
+ statePath: '/tmp/hypertwist-billing.json',
+ processedEventCount: 1,
+ pricePlanMapConfigured: true,
+ productPlanMapConfigured: true,
+ webhookSecretConfigured: true,
+ },
+ runtime: {
+ mode: 'public',
+ public_origin_ready: true,
+ cookie_secure: true,
+ api_domain: 'https://hypertwist.app',
+ website_domain: 'https://hypertwist.app',
+ warnings: [],
+ errors: [],
+ },
+ })
+
+ mockGetReleaseManifest.mockResolvedValue({
+ ok: true,
+ manifest: {
+ generated_at: '2026-06-22T12:00:00.000Z',
+ support_email: 'hello@hypertwist.app',
+ public_docs_url: 'https://docs.hypertwist.app',
+ release_notes_url: 'https://notes.hypertwist.app',
+ corresponding_source_url: 'https://hypertwist.app/open-source/source.zip',
+ open_source_repo_url: 'https://github.com/hypertwist/hypertwist',
+ viewer: {
+ authenticated: true,
+ canDownload: true,
+ plan: 'operator',
+ role: 'operator',
+ accessStatus: 'active',
+ },
+ platforms: [
+ {
+ platform_key: 'windows',
+ platform: 'Windows',
+ subtitle: 'Primary shipping lane',
+ details: 'Current packaged validation is strongest on the Windows Unreal lane.',
+ configured: true,
+ channel: 'candidate',
+ version: '1.0.0',
+ build_id: 'win64-1000',
+ published_at: '2026-06-22T00:00:00.000Z',
+ file_name: 'HyperTwist-Windows.zip',
+ file_size_bytes: 1048576,
+ checksum_sha256: 'abc123',
+ download_url: 'https://downloads.hypertwist.app/windows.exe',
+ download_available: true,
+ validation_summary: {
+ lane: 'Windows Unreal packaged validation',
+ result: 'passed',
+ generated_at: '2026-06-22T01:43:08.7625247Z',
+ configuration: 'Development',
+ skip_build: true,
+ smoke_map_count: 2,
+ smoke_maps: [
+ {
+ map_url: '/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining',
+ label: 'Magic120Cell dedicated-family training map',
+ result: 'passed',
+ },
+ {
+ map_url: '/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining',
+ label: 'MagicCube5D dedicated-family training map',
+ result: 'passed',
+ },
+ ],
+ },
+ },
+ {
+ platform_key: 'macos',
+ platform: 'macOS',
+ subtitle: 'Planned distribution surface',
+ details: 'List a signed desktop build here when the package lane is opened.',
+ configured: false,
+ channel: 'preview',
+ version: null,
+ build_id: null,
+ published_at: null,
+ file_name: null,
+ file_size_bytes: null,
+ checksum_sha256: null,
+ download_url: null,
+ download_available: false,
+ },
+ ],
+ },
+ })
+ })
+
+ it('surfaces live browser-shell posture and browser-to-desktop guidance', async () => {
+ renderPage( , '/app/browser-access')
+
+ expect(await screen.findByText('Current browser posture')).toBeTruthy()
+ expect(await screen.findByText(byExactTextContent('Auth runtime mode: public', 'LI'))).toBeTruthy()
+ expect(await screen.findByText(byExactTextContent('Configured release targets: 1/2', 'LI'))).toBeTruthy()
+ expect(screen.getByRole('link', { name: 'Open dashboard' }).getAttribute('href')).toBe('/app')
+ expect(screen.getByRole('link', { name: 'Protected notices' }).getAttribute('href')).toBe('/app/notices')
+ })
+
+ it('surfaces account release access and protected follow-through links', async () => {
+ renderPage( , '/app/account')
+
+ expect(await screen.findByText('Session profile')).toBeTruthy()
+ expect(screen.getByText(byExactTextContent('Billing source: session', 'LI'))).toBeTruthy()
+ expect(await screen.findByText(byExactTextContent('Manifest viewer access: active', 'LI'))).toBeTruthy()
+ expect(await screen.findByText(byExactTextContent('Windows: Primary shipping lane (Version 1.0.0, Channel candidate)', 'LI'))).toBeTruthy()
+ expect(screen.getByRole('link', { name: 'Open downloads' }).getAttribute('href')).toBe('/app/downloads')
+ expect(screen.getByRole('link', { name: 'Get help' }).getAttribute('href')).toBe('/support?topic=operator-access')
+ })
+
+ it('surfaces protected notices references and release-target notice posture', async () => {
+ renderPage( , '/app/notices')
+
+ expect(await screen.findByText('Distribution notices')).toBeTruthy()
+ expect(await screen.findAllByText(byExactTextContent('Configured release targets: 1/2', 'LI'))).toHaveLength(1)
+ expect(screen.getByText(byExactTextContent('Public repository/notices URL: https://github.com/hypertwist/hypertwist', 'LI'))).toBeTruthy()
+ expect(screen.getByText(byExactTextContent('Corresponding-source URL: https://hypertwist.app/open-source/source.zip', 'LI'))).toBeTruthy()
+ expect(screen.getByText(byExactTextContent('Windows: configured, validation passed', 'LI'))).toBeTruthy()
+ expect(screen.getByRole('link', { name: 'Public notices' }).getAttribute('href')).toBe('/open-source-notices')
+ })
+})
diff --git a/website/src/pages/app-pages.tsx b/website/src/pages/app-pages.tsx
index f0cdee2..5078aee 100644
--- a/website/src/pages/app-pages.tsx
+++ b/website/src/pages/app-pages.tsx
@@ -1,14 +1,14 @@
import { useMemo } from 'react'
import { useMutation, useQuery } from '@tanstack/react-query'
-import { useSearchParams } from 'react-router-dom'
+import { Link, useSearchParams } from 'react-router-dom'
import { buildAuthApiBaseUrls, createDesktopLinkToken, getAuthHealth, getReleaseManifest } from '../auth/auth-api'
import { usePlatformAuth } from '../auth/platform-auth'
import { SiteMetadata } from '../components/seo/SiteMetadata'
import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary'
import { resolvePublicLaunchReadiness } from '../public-launch'
import { downloadTargets, launchReadiness, mplSourceUrl, openSourceRepoUrl, planCatalog, publicDocsUrl, releaseNotesUrl } from '../site-config'
-import { buildReleaseMetadataItems, resolveReleaseCommerceView, resolveReleaseManifestView } from '../release-manifest'
-import { getDownloadPlatformLabel, normalizeDownloadPlatform } from '../site-routes'
+import { buildReleaseMetadataItems, resolveReleaseCommerceView, resolveReleaseManifestView, type ReleaseManifestView } from '../release-manifest'
+import { buildSupportPath, getDownloadPlatformLabel, normalizeDownloadPlatform } from '../site-routes'
import { desktopDownloadSteps, roadmapHonestyCards } from '../site-data'
function Panel({
@@ -49,17 +49,84 @@ function buildProtectedReleaseManifestFallback(supportEmail: string) {
}
}
-export function DashboardOverviewPage() {
- const { user, superTokensConfigured } = usePlatformAuth()
- const healthQuery = useQuery({
- queryKey: ['auth-health'],
+function useProtectedAuthHealthQuery(scope: string) {
+ return useQuery({
+ queryKey: ['auth-health', scope],
queryFn: getAuthHealth,
+ retry: false,
})
- const releaseManifestQuery = useQuery({
- queryKey: ['release-manifest', 'dashboard', user?.id || 'anonymous', user?.canDownload === true ? 'download' : 'nodownload'],
+}
+
+function useProtectedReleaseManifestQuery(scope: string, userId: string | undefined, canDownload: boolean) {
+ return useQuery({
+ queryKey: ['release-manifest', scope, userId || 'anonymous', canDownload ? 'download' : 'nodownload'],
queryFn: getReleaseManifest,
retry: false,
})
+}
+
+function ReleaseAuthorityLinks({
+ releaseManifest,
+ includePublicNoticesLink = false,
+ includeProtectedNoticesLink = false,
+}: {
+ releaseManifest: ReleaseManifestView
+ includePublicNoticesLink?: boolean
+ includeProtectedNoticesLink?: boolean
+}) {
+ const hasLinks = Boolean(
+ releaseManifest.public_docs_url
+ || releaseManifest.release_notes_url
+ || releaseManifest.corresponding_source_url
+ || releaseManifest.open_source_repo_url
+ || includePublicNoticesLink
+ || includeProtectedNoticesLink,
+ )
+
+ if (!hasLinks) {
+ return null
+ }
+
+ return (
+
+ {releaseManifest.public_docs_url ? (
+
+ Public docs
+
+ ) : null}
+ {releaseManifest.release_notes_url ? (
+
+ Release notes
+
+ ) : null}
+ {releaseManifest.corresponding_source_url ? (
+
+ Corresponding source
+
+ ) : null}
+ {releaseManifest.open_source_repo_url ? (
+
+ Repository/notices
+
+ ) : null}
+ {includeProtectedNoticesLink ? (
+
+ Protected notices
+
+ ) : null}
+ {includePublicNoticesLink ? (
+
+ Public notices
+
+ ) : null}
+
+ )
+}
+
+export function DashboardOverviewPage() {
+ const { user, superTokensConfigured } = usePlatformAuth()
+ const healthQuery = useProtectedAuthHealthQuery('dashboard')
+ const releaseManifestQuery = useProtectedReleaseManifestQuery('dashboard', user?.id, user?.canDownload === true)
const desktopLinkMutation = useMutation({
mutationFn: createDesktopLinkToken,
@@ -281,11 +348,7 @@ export function DownloadCenterPage() {
const [searchParams] = useSearchParams()
const canDownload = user?.canDownload === true
const requestedPlatform = normalizeDownloadPlatform(searchParams.get('platform'))
- const releaseManifestQuery = useQuery({
- queryKey: ['release-manifest', 'protected', user?.id || 'anonymous', canDownload ? 'download' : 'nodownload'],
- queryFn: getReleaseManifest,
- retry: false,
- })
+ const releaseManifestQuery = useProtectedReleaseManifestQuery('protected', user?.id, canDownload)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
@@ -425,6 +488,21 @@ export function DownloadCenterPage() {
}
export function BrowserAccessPage() {
+ const { user } = usePlatformAuth()
+ const healthQuery = useProtectedAuthHealthQuery('browser-access')
+ const releaseManifestQuery = useProtectedReleaseManifestQuery('browser-access', user?.id, user?.canDownload === true)
+ const releaseManifest = useMemo(
+ () => resolveReleaseManifestView(
+ releaseManifestQuery.data?.manifest,
+ buildProtectedReleaseManifestFallback(user?.email || 'hello@hypertwist.app'),
+ ),
+ [releaseManifestQuery.data?.manifest, user?.email],
+ )
+ const configuredPlatformCount = useMemo(
+ () => releaseManifest.platforms.filter((platform) => platform.configured).length,
+ [releaseManifest.platforms],
+ )
+
return (
<>
-
- The embedded browser/CEF shell is live and owned as part of the current shipping posture.
- Browser runtime status, runtime-ready typing, and native operator diagnostics are already surfaced in the desktop runtime.
- The public browser dashboard is for operator/account/release access, not a claim of full simulator parity.
-
-
+
+ The embedded browser/CEF shell is live and owned as part of the current shipping posture.
+ Browser runtime status, runtime-ready typing, and native operator diagnostics are already surfaced in the desktop runtime.
+ The public browser dashboard is for operator/account/release access, not a claim of full simulator parity.
+
+ {healthQuery.isLoading ?
Checking current browser-shell auth posture...
: null}
+ {healthQuery.isError ? (
+
+ Runtime auth-health could not be loaded. The page is still using the bounded dashboard posture rather than claiming browser-simulator parity.
+
+ ) : null}
+ {healthQuery.data ? (
+
+ Auth runtime mode: {healthQuery.data.runtime.mode}
+ Public auth origin ready: {healthQuery.data.runtime.public_origin_ready ? 'yes' : 'no'}
+ Fallback currently active: {healthQuery.data.fallback.active ? 'yes' : 'no'}
+ Session plan: {user?.plan || 'not resolved yet'}
+ Desktop download entitlement: {user?.canDownload ? 'enabled' : 'not yet entitled'}
+
+ ) : null}
+
+
+
+ The browser shell exists to resolve account, release, and pairing state before the desktop runtime takes over the real simulator job.
+
+
+ {desktopDownloadSteps.map((step) => (
+ {step}
+ ))}
+
+
+
+ Open dashboard
+
+
+ Open downloads
+
+
+
+
+ {releaseManifestQuery.isError ? (
+
+ Live release-manifest lookup failed. Browser-shell release context is currently using fallback metadata instead of current auth-server authority.
+
+ ) : null}
+
+ Configured release targets: {configuredPlatformCount}/{releaseManifest.platforms.length}
+ Manifest viewer access: {releaseManifest.viewer.accessStatus || 'not resolved yet'}
+ Support contact: {releaseManifest.support_email}
+
+
+
-
- The optional full-browser client remains spec-only.
- MagicTile native renderer widening remains explicit No-Go until a real browser-host gap is proven.
- This dashboard intentionally stays above those boundaries instead of silently crossing them.
-
+
+ The optional full-browser client remains spec-only.
+ MagicTile native renderer widening remains explicit No-Go until a real browser-host gap is proven.
+ This dashboard intentionally stays above those boundaries instead of silently crossing them.
+
+
+ {roadmapHonestyCards.map((item) => (
+ {item}
+ ))}
+
>
@@ -455,6 +584,18 @@ export function BrowserAccessPage() {
export function AccountPage() {
const { user, superTokensConfigured } = usePlatformAuth()
+ const releaseManifestQuery = useProtectedReleaseManifestQuery('account', user?.id, user?.canDownload === true)
+ const releaseManifest = useMemo(
+ () => resolveReleaseManifestView(
+ releaseManifestQuery.data?.manifest,
+ buildProtectedReleaseManifestFallback(user?.email || 'hello@hypertwist.app'),
+ ),
+ [releaseManifestQuery.data?.manifest, user?.email],
+ )
+ const configuredPlatforms = useMemo(
+ () => releaseManifest.platforms.filter((platform) => platform.configured),
+ [releaseManifest.platforms],
+ )
return (
<>
@@ -466,14 +607,63 @@ export function AccountPage() {
/>
-
- Email: {user?.email}
- Name: {user?.name}
- Plan: {user?.plan}
- Desktop downloads: {user?.canDownload ? 'enabled' : 'not yet entitled'}
- Billing status: {user?.billing?.accessStatus || 'session-default'}
- Auth stack: {superTokensConfigured ? 'SuperTokens-backed' : 'Local fallback mode'}
-
+
+ Email: {user?.email}
+ Name: {user?.name}
+ Plan: {user?.plan}
+ Role: {user?.role || 'operator'}
+ Desktop downloads: {user?.canDownload ? 'enabled' : 'not yet entitled'}
+ Billing status: {user?.billing?.accessStatus || 'session-default'}
+ Billing source: {user?.billing?.source || 'session'}
+ Auth stack: {superTokensConfigured ? 'SuperTokens-backed' : 'Local fallback mode'}
+
+
+
+ {releaseManifestQuery.isLoading ? Refreshing release-access posture from the live manifest...
: null}
+ {releaseManifestQuery.isError ? (
+
+ Live release-manifest lookup failed. This account view is using bounded fallback metadata until auth-server release authority returns.
+
+ ) : null}
+
+ Manifest viewer authenticated: {releaseManifest.viewer.authenticated ? 'yes' : 'no'}
+ Manifest viewer plan: {releaseManifest.viewer.plan || user?.plan || 'not resolved yet'}
+ Manifest viewer access: {releaseManifest.viewer.accessStatus || 'not resolved yet'}
+ Configured release targets: {configuredPlatforms.length}/{releaseManifest.platforms.length}
+
+ {configuredPlatforms.length > 0 ? (
+
+ {configuredPlatforms.map((platform) => {
+ const metadataItems = buildReleaseMetadataItems(platform)
+ const compactMetadata = metadataItems.slice(0, 2).map((item) => `${item.label} ${item.value}`).join(', ')
+ return (
+
+ {platform.platform}: {platform.subtitle}{compactMetadata ? ` (${compactMetadata})` : ''}
+
+ )
+ })}
+
+ ) : null}
+
+
+
+ Use the dashboard when you need a desktop-link token, the download center when you need an entitled package, and the notices lane when you need distribution/legal references.
+
+
+
+ Open dashboard
+
+
+ Open downloads
+
+
+ Review notices
+
+
+ Get help
+
+
+
>
@@ -482,11 +672,7 @@ export function AccountPage() {
export function NoticesPage() {
const { user } = usePlatformAuth()
- const releaseManifestQuery = useQuery({
- queryKey: ['release-manifest', 'protected', user?.id || 'anonymous', user?.canDownload === true ? 'download' : 'nodownload'],
- queryFn: getReleaseManifest,
- retry: false,
- })
+ const releaseManifestQuery = useProtectedReleaseManifestQuery('notices', user?.id, user?.canDownload === true)
const releaseManifest = useMemo(
() => resolveReleaseManifestView(
releaseManifestQuery.data?.manifest,
@@ -494,6 +680,10 @@ export function NoticesPage() {
),
[releaseManifestQuery.data?.manifest, user?.email],
)
+ const configuredPlatforms = useMemo(
+ () => releaseManifest.platforms.filter((platform) => platform.configured),
+ [releaseManifest.platforms],
+ )
return (
<>
@@ -505,12 +695,47 @@ export function NoticesPage() {
/>
-
- Pricing, checkout, and download surfaces must expose open-source notices and corresponding-source guidance whenever shipped builds contain MPL-covered material.
-
-
- Current corresponding-source URL: {releaseManifest.corresponding_source_url || 'configure the corresponding-source URL before public launch'}
-
+
+ Pricing, checkout, and download surfaces must expose open-source notices and corresponding-source guidance whenever shipped builds contain MPL-covered material.
+
+ {releaseManifestQuery.isLoading ? Refreshing notices posture from the live release manifest...
: null}
+ {releaseManifestQuery.isError ? (
+
+ Live release-manifest lookup failed. This page is using bounded fallback notice metadata until auth-server release authority returns.
+
+ ) : null}
+
+ Configured release targets: {configuredPlatforms.length}/{releaseManifest.platforms.length}
+ Viewer download access: {releaseManifest.viewer.canDownload ? 'enabled' : 'not yet enabled'}
+ Public repository/notices URL: {releaseManifest.open_source_repo_url || 'configure the public repository/notices URL before launch'}
+ Corresponding-source URL: {releaseManifest.corresponding_source_url || 'configure the corresponding-source URL before public launch'}
+
+
+
+
+ Keep pricing, download, and notices surfaces aligned whenever a public downloadable build is offered.
+ Keep release notes and package proof reachable so public audiences can see what build posture is actually being distributed.
+ Do not present browser access as simulator parity just because distribution/legal pages are live.
+
+ {releaseManifest.platforms.length > 0 ? (
+
+ {releaseManifest.platforms.map((platform) => (
+
+ {platform.platform}: {platform.configured ? 'configured' : 'not configured'}{platform.validation_summary ? `, validation ${platform.validation_summary.result}` : ''}
+
+ ))}
+
+ ) : null}
+
+
+
+ Operators should keep public notices, protected notices, release notes, and corresponding source reachable from the same release story.
+
+
>