Refresh package proof and harden web auth continuity
This commit is contained in:
parent
44462d98a2
commit
f801333133
22 changed files with 1580 additions and 334 deletions
|
|
@ -1,93 +1,93 @@
|
|||
{
|
||||
"reportVersion": "ht-higher-dimensional-package-validation/v1",
|
||||
"generatedAtUtc": "2026-06-23T10:27:31.6439999Z",
|
||||
"projectRoot": "C:/HyperTwist_worktrees/phase10validate",
|
||||
"archiveDirectory": "C:/HyperTwist_worktrees/phase10validate_packaged_phase6c_higherdim_refresh_20260623",
|
||||
"configuration": "Development",
|
||||
"cookMaps": [
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining"
|
||||
],
|
||||
"smokeMaps": [
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining"
|
||||
],
|
||||
"additionalCookerOptions": [
|
||||
"-DisablePlugins=MovieRenderPipeline",
|
||||
"-SkipCookingEditorContent"
|
||||
],
|
||||
"cleanArchive": true,
|
||||
"skipBuild": true,
|
||||
"skipLaunch": false,
|
||||
"authoringManifest": {
|
||||
"path": "C:\\HyperTwist_worktrees\\phase10validate\\docs\\generated\\higher_dimensional_training_maps\\phase6c_dedicated_family_map_manifest.json",
|
||||
"manifestId": "phase6c/dedicated-family-training-map-authoring",
|
||||
"manifestVersion": "2026.06.18",
|
||||
"authoredThroughGameModeClassPath": "/Script/UnrealHyperTwist.HyperTwistCoachDashboardGameMode",
|
||||
"validatedEntries": [
|
||||
{
|
||||
"mapKind": "magic120cell",
|
||||
"familyKey": "magic120cell",
|
||||
"mapAssetPath": "/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"mapFileRelativePath": "UnrealHyperTwist/Content/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining.umap",
|
||||
"mapHashMd5": "9d9b9301f14cdad8c263d2c57b5f2c7f",
|
||||
"activationProfileId": "magic120cell-cleanroom-runtime-activation",
|
||||
"runtimeModeId": "magic120cell-full-color-runtime-v1",
|
||||
"projectionProfileId": "magic120cell-4d-projection-distance-v1",
|
||||
"primaryPersistenceBoundaryId": "magic120cell-persistence-boundary"
|
||||
},
|
||||
{
|
||||
"mapKind": "magiccube5d",
|
||||
"familyKey": "magiccube5d",
|
||||
"mapAssetPath": "/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining",
|
||||
"mapFileRelativePath": "UnrealHyperTwist/Content/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining.umap",
|
||||
"mapHashMd5": "15d2f7acfdce43402ad2fd291fc5c781",
|
||||
"activationProfileId": "magiccube5d-cleanroom-runtime-activation",
|
||||
"runtimeModeId": "magiccube5d-order3-runtime-v1",
|
||||
"projectionProfileId": "magiccube5d-5d-projection-distance-v1",
|
||||
"primaryPersistenceBoundaryId": "magiccube5d-persistence-boundary"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": "passed",
|
||||
"packagedExecutablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260623\\Windows\\UnrealHyperTwist.exe",
|
||||
"smokeReports": [
|
||||
{
|
||||
"reportVersion": "ht-higher-dimensional-package-smoke/v1",
|
||||
"generatedAtUtc": "2026-06-23T10:28:44.7973129Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260623",
|
||||
"executablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260623\\Windows\\UnrealHyperTwist.exe",
|
||||
"mapUrl": "/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"smokeSeconds": 10,
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"keepRunning": false,
|
||||
"result": "passed",
|
||||
"processId": 20320,
|
||||
"processStopped": true,
|
||||
"exitCode": null,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"reportVersion": "ht-higher-dimensional-package-smoke/v1",
|
||||
"generatedAtUtc": "2026-06-23T10:28:55.0662545Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260623",
|
||||
"executablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260623\\Windows\\UnrealHyperTwist.exe",
|
||||
"mapUrl": "/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining",
|
||||
"smokeSeconds": 10,
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"keepRunning": false,
|
||||
"result": "passed",
|
||||
"processId": 13584,
|
||||
"processStopped": true,
|
||||
"exitCode": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"error": null
|
||||
}
|
||||
"reportVersion": "ht-higher-dimensional-package-validation/v1",
|
||||
"generatedAtUtc": "2026-06-24T12:53:32.7234772Z",
|
||||
"projectRoot": "C:\\HyperTwist_worktrees\\phase10validate",
|
||||
"archiveDirectory": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624",
|
||||
"configuration": "Development",
|
||||
"cookMaps": [
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining"
|
||||
],
|
||||
"smokeMaps": [
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining"
|
||||
],
|
||||
"additionalCookerOptions": [
|
||||
"-DisablePlugins=MovieRenderPipeline",
|
||||
"-SkipCookingEditorContent"
|
||||
],
|
||||
"cleanArchive": true,
|
||||
"skipBuild": true,
|
||||
"skipLaunch": false,
|
||||
"authoringManifest": {
|
||||
"path": "C:\\HyperTwist_worktrees\\phase10validate\\docs\\generated\\higher_dimensional_training_maps\\phase6c_dedicated_family_map_manifest.json",
|
||||
"manifestId": "phase6c/dedicated-family-training-map-authoring",
|
||||
"manifestVersion": "2026.06.18",
|
||||
"authoredThroughGameModeClassPath": "/Script/UnrealHyperTwist.HyperTwistCoachDashboardGameMode",
|
||||
"validatedEntries": [
|
||||
{
|
||||
"mapKind": "magic120cell",
|
||||
"familyKey": "magic120cell",
|
||||
"mapAssetPath": "/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"mapFileRelativePath": "UnrealHyperTwist/Content/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining.umap",
|
||||
"mapHashMd5": "9d9b9301f14cdad8c263d2c57b5f2c7f",
|
||||
"activationProfileId": "magic120cell-cleanroom-runtime-activation",
|
||||
"runtimeModeId": "magic120cell-full-color-runtime-v1",
|
||||
"projectionProfileId": "magic120cell-4d-projection-distance-v1",
|
||||
"primaryPersistenceBoundaryId": "magic120cell-persistence-boundary"
|
||||
},
|
||||
{
|
||||
"mapKind": "magiccube5d",
|
||||
"familyKey": "magiccube5d",
|
||||
"mapAssetPath": "/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining",
|
||||
"mapFileRelativePath": "UnrealHyperTwist/Content/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining.umap",
|
||||
"mapHashMd5": "15d2f7acfdce43402ad2fd291fc5c781",
|
||||
"activationProfileId": "magiccube5d-cleanroom-runtime-activation",
|
||||
"runtimeModeId": "magiccube5d-order3-runtime-v1",
|
||||
"projectionProfileId": "magiccube5d-5d-projection-distance-v1",
|
||||
"primaryPersistenceBoundaryId": "magiccube5d-persistence-boundary"
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": "passed",
|
||||
"packagedExecutablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624\\Windows\\UnrealHyperTwist.exe",
|
||||
"smokeReports": [
|
||||
{
|
||||
"reportVersion": "ht-higher-dimensional-package-smoke/v1",
|
||||
"generatedAtUtc": "2026-06-24T12:54:48.8912848Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624",
|
||||
"executablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624\\Windows\\UnrealHyperTwist.exe",
|
||||
"mapUrl": "/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining",
|
||||
"smokeSeconds": 10,
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"keepRunning": false,
|
||||
"result": "passed",
|
||||
"processId": 34956,
|
||||
"processStopped": true,
|
||||
"exitCode": null,
|
||||
"error": null
|
||||
},
|
||||
{
|
||||
"reportVersion": "ht-higher-dimensional-package-smoke/v1",
|
||||
"generatedAtUtc": "2026-06-24T12:54:59.1871228Z",
|
||||
"packageRoot": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624",
|
||||
"executablePath": "C:\\HyperTwist_worktrees\\phase10validate_packaged_phase6c_higherdim_refresh_20260624\\Windows\\UnrealHyperTwist.exe",
|
||||
"mapUrl": "/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining",
|
||||
"smokeSeconds": 10,
|
||||
"resolution": {
|
||||
"width": 1600,
|
||||
"height": 900
|
||||
},
|
||||
"keepRunning": false,
|
||||
"result": "passed",
|
||||
"processId": 34432,
|
||||
"processStopped": true,
|
||||
"exitCode": null,
|
||||
"error": null
|
||||
}
|
||||
],
|
||||
"error": null
|
||||
}
|
||||
|
|
@ -118,6 +118,10 @@ Current packaged-proof bridge truth after the same `2026-06-23` continuation:
|
|||
- the `2026-06-24` centralized public-route-authority packet then kept the
|
||||
same umbrella gate green while moving navigation, footer links, router
|
||||
entries, and sitemap generation onto a shared route registry
|
||||
- `scripts/run-hypertwist-remote-windows-file-pull.sh` now gives the lane a
|
||||
bounded first-party way to pull Windows-side validation artifacts back into
|
||||
the HyperTwist repo with explicit remote-to-local path mapping and SHA-256
|
||||
verification instead of relying on ad hoc manual copies
|
||||
|
||||
Current bounded refactor-tool truth after the `2026-06-24` follow-up:
|
||||
|
||||
|
|
@ -242,6 +246,48 @@ Latest follow-up after that still on `2026-06-24`:
|
|||
- remaining structural work is now smaller-seam cleanup or broader
|
||||
product-quality hardening, not another big skill-validator extraction
|
||||
|
||||
Latest later higher-dimensional proof refresh still on `2026-06-24`:
|
||||
|
||||
- the maintained Windows higher-dimensional package lane was rerun end to end
|
||||
through:
|
||||
- `Invoke-HyperTwistHigherDimensionalPackage.ps1`
|
||||
- maintained validation root:
|
||||
`C:\HyperTwist_worktrees\phase10validate`
|
||||
- fresh archive root:
|
||||
`C:\HyperTwist_worktrees\phase10validate_packaged_phase6c_higherdim_refresh_20260624`
|
||||
- the refreshed authority report now records:
|
||||
- `generatedAtUtc: 2026-06-24T12:53:32.7234772Z`
|
||||
- `result: passed`
|
||||
|
||||
Latest same-day browser/distribution continuity follow-up still on `2026-06-24`:
|
||||
|
||||
- auth-entry, protected-route loading, dashboard auth health, and release-authority fallback states now share a more deliberate operator-facing recovery shape instead of scattering single-line warnings across public and protected surfaces
|
||||
- the new shared callout posture explicitly separates:
|
||||
- what still works
|
||||
- what stays intentionally withheld
|
||||
- recommended recovery order
|
||||
- current widened surfaces include:
|
||||
- login and register auth-runtime degradation guidance
|
||||
- protected-route loading with preserved next-target sign-in and support escape hatches
|
||||
- dashboard auth-health and release-authority fallback continuity
|
||||
- public download and protected release-lane fallback continuity
|
||||
- this hardening is still boundary-honest:
|
||||
- degraded browser auth does not become a false production claim
|
||||
- degraded release-manifest lookup does not unlock raw package delivery
|
||||
- desktop-first simulator truth remains unchanged while browser/operator guidance improves
|
||||
- packaged executable:
|
||||
`C:\HyperTwist_worktrees\phase10validate_packaged_phase6c_higherdim_refresh_20260624\Windows\UnrealHyperTwist.exe`
|
||||
- packaged smoke validation also passed for both dedicated-family maps:
|
||||
- `/Game/HyperTwistTraining/Maps/L_HyperTwist_Magic120CellTraining`
|
||||
- `/Game/HyperTwistTraining/Maps/L_HyperTwist_MagicCube5DTraining`
|
||||
- the new pull helper then brought the refreshed report back into:
|
||||
- `docs/generated/higher_dimensional_training_maps/phase6c_dedicated_family_package_validation_report.json`
|
||||
- `scripts/render-hypertwist-web-package-validation-summary.mjs` was rerun
|
||||
immediately afterward, so
|
||||
`website/src/shared/generated/windows-package-validation-summary.json`
|
||||
now reflects the same fresh proof date instead of the earlier
|
||||
`2026-06-23` report
|
||||
|
||||
## Canonical development authorities
|
||||
|
||||
Use these before widening implementation:
|
||||
|
|
|
|||
163
scripts/run-hypertwist-remote-windows-file-pull.sh
Normal file
163
scripts/run-hypertwist-remote-windows-file-pull.sh
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
|
||||
scripts/run-hypertwist-remote-windows-file-pull.sh \
|
||||
--map "C:\HyperTwist_worktrees\phase10validate\docs\generated\higher_dimensional_training_maps\phase6c_dedicated_family_package_validation_report.json::docs/generated/higher_dimensional_training_maps/phase6c_dedicated_family_package_validation_report.json"
|
||||
|
||||
Options:
|
||||
--map <remote_windows_path>::<local_repo_path>
|
||||
Copy one remote Windows file back into the local repo. May be repeated.
|
||||
|
||||
--dry-run
|
||||
Print the pull plan without transferring files.
|
||||
|
||||
Environment:
|
||||
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required password for the reverse-SSH Windows user.
|
||||
HYPERTWIST_REMOTE_TUNNEL_PORT Optional, defaults to 22022.
|
||||
HYPERTWIST_REMOTE_WINDOWS_USER Optional, defaults to "anthracite ace".
|
||||
HYPERTWIST_REMOTE_TUNNEL_HOST Optional, defaults to "localhost".
|
||||
EOF
|
||||
}
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "python3 is required but was not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v sha256sum >/dev/null 2>&1; then
|
||||
echo "sha256sum is required but was not found on PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
remote_ps_wrapper="${repo_root}/scripts/run-hypertwist-remote-windows-powershell.sh"
|
||||
|
||||
if [[ ! -x "$remote_ps_wrapper" ]]; then
|
||||
echo "Expected executable wrapper at $remote_ps_wrapper" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dry_run="false"
|
||||
maps=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--map)
|
||||
maps+=("${2:-}")
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run="true"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ${#maps[@]} -eq 0 ]]; then
|
||||
echo "Provide at least one --map." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ps_single_quote() {
|
||||
printf "'%s'" "${1//\'/\'\'}"
|
||||
}
|
||||
|
||||
write_base64_to_file() {
|
||||
local base64_payload="$1"
|
||||
local destination_path="$2"
|
||||
|
||||
python3 - <<'PY' "$base64_payload" "$destination_path"
|
||||
import base64
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
payload = sys.argv[1]
|
||||
destination = pathlib.Path(sys.argv[2])
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if payload:
|
||||
destination.write_bytes(base64.b64decode(payload.encode("ascii")))
|
||||
else:
|
||||
destination.write_bytes(b"")
|
||||
PY
|
||||
}
|
||||
|
||||
for mapping in "${maps[@]}"; do
|
||||
if [[ "$mapping" != *"::"* ]]; then
|
||||
echo "Map must use <remote_windows_path>::<local_repo_path>: $mapping" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
remote_path="${mapping%%::*}"
|
||||
local_path="${mapping#*::}"
|
||||
|
||||
if [[ -z "$remote_path" || -z "$local_path" ]]; then
|
||||
echo "Map must include both remote and local paths: $mapping" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$local_path" = /* ]]; then
|
||||
local_destination="$local_path"
|
||||
else
|
||||
local_destination="${repo_root}/${local_path}"
|
||||
fi
|
||||
|
||||
if [[ "$dry_run" == "true" ]]; then
|
||||
printf '%s -> %s\n' "$remote_path" "$local_destination"
|
||||
continue
|
||||
fi
|
||||
|
||||
remote_payload="$(
|
||||
cat <<EOF
|
||||
\$Path = $(ps_single_quote "$remote_path")
|
||||
if (-not (Test-Path -LiteralPath \$Path)) {
|
||||
throw "Remote file was not found at '\$Path'."
|
||||
}
|
||||
\$Bytes = [System.IO.File]::ReadAllBytes(\$Path)
|
||||
\$Hash = (Get-FileHash -LiteralPath \$Path -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
Write-Output ('REMOTE_HASH=' + \$Hash)
|
||||
Write-Output 'REMOTE_BASE64_BEGIN'
|
||||
[Console]::Out.Write([Convert]::ToBase64String(\$Bytes))
|
||||
Write-Output ''
|
||||
Write-Output 'REMOTE_BASE64_END'
|
||||
EOF
|
||||
)"
|
||||
|
||||
remote_output="$("$remote_ps_wrapper" --command "$remote_payload" | tr -d '\r')"
|
||||
remote_hash="$(printf '%s\n' "$remote_output" | sed -n 's/^REMOTE_HASH=//p' | head -n 1)"
|
||||
|
||||
if [[ -z "$remote_hash" ]]; then
|
||||
echo "Did not receive REMOTE_HASH for $remote_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base64_payload="$(
|
||||
printf '%s\n' "$remote_output" \
|
||||
| awk '/^REMOTE_BASE64_BEGIN$/{flag=1; next} /^REMOTE_BASE64_END$/{flag=0} flag' \
|
||||
| tr -d '\n'
|
||||
)"
|
||||
|
||||
write_base64_to_file "$base64_payload" "$local_destination"
|
||||
|
||||
local_hash="$(sha256sum "$local_destination" | awk '{print tolower($1)}')"
|
||||
if [[ "$local_hash" != "$remote_hash" ]]; then
|
||||
echo "Hash mismatch for $remote_path" >&2
|
||||
echo " remote: $remote_hash" >&2
|
||||
echo " local : $local_hash" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'Pulled %s -> %s (%s)\n' "$remote_path" "$local_destination" "$local_hash"
|
||||
done
|
||||
|
|
@ -180,6 +180,8 @@ describe('DashboardOverviewPage', () => {
|
|||
|
||||
expect(screen.getByText(/This session is currently using local fallback posture/i)).toBeTruthy()
|
||||
expect(screen.getByText(/Shared auth core is not fully ready right now/i)).toBeTruthy()
|
||||
expect(screen.getByText('What stays intentionally bounded')).toBeTruthy()
|
||||
expect(screen.getByText('Recommended recovery order')).toBeTruthy()
|
||||
expect(screen.getByText(/Public launch is not fully configured yet:/i)).toBeTruthy()
|
||||
expect(screen.getByText('Billing product-plan map: missing')).toBeTruthy()
|
||||
expect(screen.getByText('Billing price-plan map: missing')).toBeTruthy()
|
||||
|
|
|
|||
|
|
@ -200,10 +200,12 @@ describe('DownloadCenterPage', () => {
|
|||
expect(screen.getByText('Release guide')).toBeTruthy()
|
||||
expect(screen.getByText('First launch follow-through')).toBeTruthy()
|
||||
expect(screen.getByText('Verify the current training lanes')).toBeTruthy()
|
||||
expect(screen.getByText('Download and rollout escalation')).toBeTruthy()
|
||||
expect(screen.getByText('Current product-surface map')).toBeTruthy()
|
||||
expect(await screen.findByText('Version: 1.0.0')).toBeTruthy()
|
||||
expect(screen.getByText('Build: win64-1000')).toBeTruthy()
|
||||
expect(screen.getByText('Packaged validation passed')).toBeTruthy()
|
||||
expect(screen.getByText(/MagicCube5D dedicated-family training map: passed/i)).toBeTruthy()
|
||||
expect(screen.getAllByText('Packaged validation passed').length).toBeGreaterThan(0)
|
||||
expect(screen.getAllByText(/MagicCube5D dedicated-family training map: passed/i).length).toBeGreaterThan(0)
|
||||
expect(document.title).toBe('HyperTwist downloads | HyperTwist')
|
||||
})
|
||||
|
||||
|
|
@ -315,6 +317,8 @@ describe('DownloadCenterPage', () => {
|
|||
expect(await screen.findByText(/fallback release posture/i)).toBeTruthy()
|
||||
expect(screen.getByText(/your signed-in account still resolves to desktop access/i)).toBeTruthy()
|
||||
expect(screen.getAllByText('Live release authority temporarily unavailable').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('What stays intentionally withheld')).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Open dashboard' }).getAttribute('href')).toBe('/app')
|
||||
expect(screen.queryByText(/account not entitled yet/i)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,27 +1,11 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import generatedWindowsValidationSummary from '../shared/generated/windows-package-validation-summary.json'
|
||||
import { getReleasePlatformValidationSummary } from '../shared/package-validation'
|
||||
|
||||
describe('getReleasePlatformValidationSummary', () => {
|
||||
it('reads the current Windows packaged-validation truth from the generated higher-dimensional report summary', () => {
|
||||
expect(getReleasePlatformValidationSummary('windows')).toMatchObject({
|
||||
lane: 'Windows Unreal packaged validation',
|
||||
result: 'passed',
|
||||
generated_at: '2026-06-23T10:27:31.6439999Z',
|
||||
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',
|
||||
},
|
||||
],
|
||||
})
|
||||
expect(getReleasePlatformValidationSummary('windows')).toEqual(generatedWindowsValidationSummary)
|
||||
})
|
||||
|
||||
it('keeps non-Windows release lanes free of fake packaged-validation summaries', () => {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ vi.mock('../auth/auth-api', () => ({
|
|||
getReleaseManifest: (...args: unknown[]) => mockGetReleaseManifest(...args),
|
||||
}))
|
||||
|
||||
import { AccountPage, BrowserAccessPage, NoticesPage } from '../pages/app-pages'
|
||||
import { AccountPage, BrowserAccessPage, DownloadCenterPage, NoticesPage } from '../pages/app-pages'
|
||||
|
||||
function renderPage(page: React.ReactNode, initialEntry: string) {
|
||||
const queryClient = new QueryClient({
|
||||
|
|
@ -205,6 +205,22 @@ describe('protected app pages', () => {
|
|||
expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access')
|
||||
})
|
||||
|
||||
it('keeps the protected download center aligned with packaged proof, notices, and escalation guidance', async () => {
|
||||
renderPage(<DownloadCenterPage />, '/app/downloads?platform=windows')
|
||||
|
||||
expect(await screen.findByText('Release targets')).toBeTruthy()
|
||||
expect(await screen.findByText('Version: 1.0.0')).toBeTruthy()
|
||||
expect(screen.getAllByText('Requested target').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(screen.getAllByText('Packaged validation passed').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Download and rollout escalation')).toBeTruthy()
|
||||
expect(screen.getByText('Current product-surface map')).toBeTruthy()
|
||||
expect(screen.getByText('Protected browser dashboard')).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Protected notices' }).getAttribute('href')).toBe('/app/notices')
|
||||
expect(screen.getByRole('link', { name: 'Public notices' }).getAttribute('href')).toBe('/open-source-notices')
|
||||
expect(screen.getByRole('link', { name: 'Download Windows' }).getAttribute('href')).toBe('https://downloads.hypertwist.app/windows.exe')
|
||||
})
|
||||
|
||||
it('surfaces account release access and protected follow-through links', async () => {
|
||||
renderPage(<AccountPage />, '/app/account')
|
||||
|
||||
|
|
@ -226,7 +242,7 @@ describe('protected app pages', () => {
|
|||
renderPage(<NoticesPage />, '/app/notices')
|
||||
|
||||
expect(await screen.findByText('Distribution notices')).toBeTruthy()
|
||||
expect(screen.getByText('Current packaged release proof')).toBeTruthy()
|
||||
expect(screen.getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(screen.getByText('Packaged validation passed')).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()
|
||||
|
|
|
|||
|
|
@ -206,6 +206,10 @@ describe('public auth and download pages', () => {
|
|||
|
||||
const registerLink = screen.getByRole('link', { name: /create an account/i })
|
||||
expect(registerLink.getAttribute('href')).toBe('/register?next=%2Fapp')
|
||||
expect(screen.getByText('Protected operator dashboard')).toBeTruthy()
|
||||
expect(screen.getAllByText(/After sign-in you will continue to the protected operator dashboard/i).length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Protected browser dashboard')).toBeTruthy()
|
||||
expect(screen.getByText('Native Unreal desktop runtime')).toBeTruthy()
|
||||
|
||||
await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
|
||||
await userEvent.type(screen.getByLabelText('Password'), 'password123')
|
||||
|
|
@ -226,6 +230,10 @@ describe('public auth and download pages', () => {
|
|||
|
||||
const loginLink = screen.getByRole('link', { name: 'Log in' })
|
||||
expect(loginLink.getAttribute('href')).toBe('/login?next=%2Fapp%2Fdownloads%3Fplatform%3Dwindows')
|
||||
expect(screen.getByText('Protected Windows release lane')).toBeTruthy()
|
||||
expect(screen.getAllByText(/After sign-in you will continue to the protected Windows download center/i).length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Protected browser dashboard')).toBeTruthy()
|
||||
expect(screen.getByText('Native Unreal desktop runtime')).toBeTruthy()
|
||||
|
||||
await userEvent.type(screen.getByLabelText('Name'), 'Operator')
|
||||
await userEvent.type(screen.getByLabelText('Email'), 'operator@hypertwist.app')
|
||||
|
|
@ -278,8 +286,13 @@ describe('public auth and download pages', () => {
|
|||
renderAuthRoutes('/login')
|
||||
|
||||
expect(screen.getByText(/Shared browser auth is not fully in production posture yet./i)).toBeTruthy()
|
||||
expect(screen.getByText('What still works')).toBeTruthy()
|
||||
expect(screen.getByText('What stays intentionally bounded')).toBeTruthy()
|
||||
expect(screen.getByText('Recommended recovery order')).toBeTruthy()
|
||||
expect(screen.getByText('Missing auth env: VITE_SUPERTOKENS_API_DOMAIN')).toBeTruthy()
|
||||
expect(screen.getByText('VITE_AUTH_API_BASE_URL not configured; same-origin auth fallback remains active.')).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access')
|
||||
expect(screen.getByText('Protected operator dashboard')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('shows enabled OAuth buttons and routes provider clicks through the login handler', async () => {
|
||||
|
|
|
|||
|
|
@ -241,19 +241,72 @@ describe('public marketing pages', () => {
|
|||
expect(screen.getByRole('link', { name: /sign in for windows access/i }).getAttribute('href')).toBe('/app/downloads?platform=windows')
|
||||
expect(screen.getAllByText('Preview posture').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Operator checkout URL: missing')).toBeTruthy()
|
||||
expect(screen.getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(screen.getByText('How the release lane works')).toBeTruthy()
|
||||
expect(screen.getByText('First launch and desktop setup')).toBeTruthy()
|
||||
expect(screen.getByText('Digital delivery workflow')).toBeTruthy()
|
||||
expect(screen.getByText('Protected entitlement handoff')).toBeTruthy()
|
||||
expect(screen.getByText('Distribution doctrine')).toBeTruthy()
|
||||
expect(screen.getByText('Public pages are distribution surfaces')).toBeTruthy()
|
||||
expect(screen.getByText('Pair desktop access to the browser account')).toBeTruthy()
|
||||
expect(await screen.findByText('Version: 1.0.0')).toBeTruthy()
|
||||
expect(screen.getByText('SHA-256: abc123')).toBeTruthy()
|
||||
expect(screen.getByText('Packaged validation passed')).toBeTruthy()
|
||||
expect(screen.getByText(/Magic120Cell dedicated-family training map: passed/i)).toBeTruthy()
|
||||
expect(screen.getAllByText('Packaged validation passed').length).toBeGreaterThan(0)
|
||||
expect(screen.getAllByText(/Magic120Cell dedicated-family training map: passed/i).length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Generates desktop-link tokens for safe browser-to-desktop handoff')).toBeTruthy()
|
||||
expect(screen.getByText('Owns the device/runtime integrations the public website does not claim')).toBeTruthy()
|
||||
expect(document.title).toBe('Download HyperTwist desktop | HyperTwist')
|
||||
expect(document.head.querySelector('meta[property="og:url"]')?.getAttribute('content')).toBe('https://hypertwist.app/download')
|
||||
})
|
||||
|
||||
it('keeps public download fallback posture explicit when live release authority is unavailable', async () => {
|
||||
mockGetAuthHealth.mockResolvedValue({
|
||||
ok: true,
|
||||
service: 'hypertwist-auth-server',
|
||||
supertokens: {
|
||||
configured: true,
|
||||
reachable: true,
|
||||
ready: true,
|
||||
apiVersion: '5.4',
|
||||
error: null,
|
||||
oauth: {
|
||||
github: false,
|
||||
google: false,
|
||||
},
|
||||
},
|
||||
fallback: {
|
||||
enabled: true,
|
||||
active: false,
|
||||
reason: null,
|
||||
},
|
||||
billing: {
|
||||
statePath: '/var/lib/hypertwist/auth/hypertwist-billing-state.json',
|
||||
processedEventCount: 0,
|
||||
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: [],
|
||||
errors: [],
|
||||
},
|
||||
})
|
||||
mockGetReleaseManifest.mockRejectedValue(new Error('release manifest unavailable'))
|
||||
|
||||
renderWithProviders(<DownloadPage />)
|
||||
|
||||
expect(await screen.findByText(/public release fallback/i)).toBeTruthy()
|
||||
expect(screen.getByText(/direct package delivery stays intentionally withheld until that authority returns/i)).toBeTruthy()
|
||||
expect(screen.getByText('What stays intentionally withheld')).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Sign in to inspect access' }).getAttribute('href')).toBe('/login')
|
||||
expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access')
|
||||
})
|
||||
|
||||
it('surfaces homepage preview posture from live auth and release readiness', async () => {
|
||||
mockGetAuthHealth.mockResolvedValue({
|
||||
ok: true,
|
||||
|
|
@ -706,7 +759,44 @@ describe('public marketing pages', () => {
|
|||
role: null,
|
||||
accessStatus: null,
|
||||
},
|
||||
platforms: [],
|
||||
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: null,
|
||||
download_available: false,
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -714,9 +804,14 @@ describe('public marketing pages', () => {
|
|||
|
||||
expect(await screen.findByText('$19 / month')).toBeTruthy()
|
||||
expect(screen.getByText('$99 / month')).toBeTruthy()
|
||||
expect(screen.getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(screen.getAllByText('What happens after access is granted').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Shows account, auth, billing, and release readiness posture')).toBeTruthy()
|
||||
expect(screen.getByText('Owns recognition, replay, coaching, and packaged training behavior')).toBeTruthy()
|
||||
expect(screen.getByText('Commercial distribution doctrine')).toBeTruthy()
|
||||
expect(screen.getByText('Public pages are distribution surfaces')).toBeTruthy()
|
||||
expect(screen.getByText('Terms of access in practice')).toBeTruthy()
|
||||
expect(screen.getByText('Access model')).toBeTruthy()
|
||||
const checkoutLinks = screen.getAllByRole('link', { name: /open paddle checkout/i })
|
||||
expect(checkoutLinks).toHaveLength(2)
|
||||
expect(checkoutLinks[0].getAttribute('href')).toBe('https://buy.paddle.com/operator-live')
|
||||
|
|
@ -790,6 +885,10 @@ describe('public marketing pages', () => {
|
|||
expect(screen.getByText('Browser and desktop responsibilities')).toBeTruthy()
|
||||
expect(screen.getByText('Shows account, auth, billing, and release readiness posture')).toBeTruthy()
|
||||
expect(screen.getByText('Support lanes')).toBeTruthy()
|
||||
expect(screen.getByText('Digital delivery workflow')).toBeTruthy()
|
||||
expect(screen.getByText('Post-install operator workflow')).toBeTruthy()
|
||||
expect(screen.getByText('Privacy and compliance boundary')).toBeTruthy()
|
||||
expect(screen.getByText('Browser identity and billing boundary')).toBeTruthy()
|
||||
expect(screen.getByText('Escalation map')).toBeTruthy()
|
||||
expect(screen.getByText('Runtime and training issues')).toBeTruthy()
|
||||
})
|
||||
|
|
@ -967,8 +1066,12 @@ describe('public marketing pages', () => {
|
|||
|
||||
renderWithProviders(<DocsPage />, ['/docs'])
|
||||
|
||||
expect(screen.getByText('Current shipped capability')).toBeTruthy()
|
||||
expect(screen.getByText('Native training and coaching core')).toBeTruthy()
|
||||
expect(screen.getByText('Operator manual')).toBeTruthy()
|
||||
expect(screen.getAllByText('Current surface authority map').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('When to use browser versus desktop')).toBeTruthy()
|
||||
expect(screen.getAllByText('Protected browser dashboard').length).toBeGreaterThan(0)
|
||||
expect(screen.getAllByText('1. Start in the browser shell').length).toBeGreaterThan(0)
|
||||
expect(screen.getAllByText('Optional full-browser simulator branch').length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Simulator manual')).toBeTruthy()
|
||||
|
|
@ -981,6 +1084,8 @@ describe('public marketing pages', () => {
|
|||
expect(screen.getAllByText(/latest persisted generated-mode selector posture is available for recall/i).length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Feature-registry-backed wording only')).toBeTruthy()
|
||||
expect(screen.getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(screen.getByText('Common operator questions')).toBeTruthy()
|
||||
expect(screen.getByText('If the desktop app is primary, why keep the web version?')).toBeTruthy()
|
||||
expect(screen.getByText('Packaged validation passed')).toBeTruthy()
|
||||
expect(await screen.findByRole('link', { name: /open public docs portal/i })).toBeTruthy()
|
||||
})
|
||||
|
|
@ -1195,17 +1300,31 @@ describe('public marketing pages', () => {
|
|||
expect(within(noticesView.container).getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(within(noticesView.container).getByText('Packaged validation passed')).toBeTruthy()
|
||||
|
||||
renderWithProviders(<PrivacyPage />, ['/privacy'])
|
||||
expect(screen.getByText('Practical privacy boundary')).toBeTruthy()
|
||||
expect(screen.getByText('Browser identity and billing boundary')).toBeTruthy()
|
||||
const privacyView = renderWithProviders(<PrivacyPage />, ['/privacy'])
|
||||
expect(within(privacyView.container).getByText('Practical privacy boundary')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Browser identity and billing boundary')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Browser and desktop responsibilities')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Current surface authority map')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Support-safe escalation boundary')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(within(privacyView.container).getByText('Packaged validation passed')).toBeTruthy()
|
||||
|
||||
renderWithProviders(<TermsPage />, ['/terms'])
|
||||
expect(screen.getByText('Terms in practice')).toBeTruthy()
|
||||
expect(screen.getByText('Access model')).toBeTruthy()
|
||||
const termsView = renderWithProviders(<TermsPage />, ['/terms'])
|
||||
expect(within(termsView.container).getByText('Terms in practice')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Access model')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Browser and desktop responsibilities')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Current surface authority map')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Release and redistribution checklist')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(within(termsView.container).getByText('Packaged validation passed')).toBeTruthy()
|
||||
|
||||
const shippingPaymentView = renderWithProviders(<ShippingPaymentPage />, ['/shipping-payment'])
|
||||
expect(within(shippingPaymentView.container).getByText('Digital delivery workflow')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Protected entitlement handoff')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('What happens after access is granted')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Terms of access in practice')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Distribution doctrine')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Release and redistribution checklist')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Current packaged desktop proof')).toBeTruthy()
|
||||
expect(within(shippingPaymentView.container).getByText('Packaged validation passed')).toBeTruthy()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -159,6 +159,9 @@ describe('website route and shell behavior', () => {
|
|||
renderProtectedRoute('/app/account')
|
||||
|
||||
expect(screen.getByText('Loading operator access...')).toBeTruthy()
|
||||
expect(screen.getByText(/Checking your saved browser session, protected release posture, and browser-to-desktop continuity/i)).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Open sign-in' }).getAttribute('href')).toBe('/login?next=%2Fapp%2Faccount')
|
||||
expect(screen.getByRole('link', { name: 'Open support' }).getAttribute('href')).toBe('/support?topic=operator-access')
|
||||
})
|
||||
|
||||
it('redirects unauthenticated protected routes to login with the full encoded next target', async () => {
|
||||
|
|
@ -243,6 +246,10 @@ describe('website route and shell behavior', () => {
|
|||
expect(screen.getByText('Operator Prime')).toBeTruthy()
|
||||
expect(screen.getByText('studio plan')).toBeTruthy()
|
||||
expect(screen.getByText('Overview content')).toBeTruthy()
|
||||
expect(screen.getByText('Desktop-first operator lane')).toBeTruthy()
|
||||
expect(screen.getByRole('link', { name: 'Public download posture' }).getAttribute('href')).toBe('/download')
|
||||
expect(screen.getByRole('link', { name: 'Public notices' }).getAttribute('href')).toBe('/open-source-notices')
|
||||
expect(screen.getByRole('link', { name: 'Operator access support' }).getAttribute('href')).toBe('/support?topic=operator-access')
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Switch to light mode' }))
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Log out' }))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Gauge, KeyRound, MonitorSmartphone, PackageOpen, ShieldCheck } from 'lucide-react'
|
||||
import { NavLink, Outlet } from 'react-router-dom'
|
||||
import { Link, NavLink, Outlet } from 'react-router-dom'
|
||||
import { usePlatformAuth } from '../../auth/platform-auth'
|
||||
import { brandConfig } from '../../site-config'
|
||||
import { buildSupportPath } from '../../site-routes'
|
||||
|
||||
const appLinks = [
|
||||
{ to: '/app', label: 'Overview', icon: Gauge, end: true },
|
||||
|
|
@ -45,6 +46,18 @@ export function AppShell() {
|
|||
})}
|
||||
</nav>
|
||||
<div className="app-sidebar__footer">
|
||||
<div className="callout app-sidebar__status">
|
||||
<p className="status-pill status-pill--info">Desktop-first operator lane</p>
|
||||
<p>
|
||||
Use this protected shell for account, release, notices, and desktop-link pairing.
|
||||
Use the native Unreal runtime for actual simulator execution.
|
||||
</p>
|
||||
<ul className="list top-gap">
|
||||
<li><Link to="/download">Public download posture</Link></li>
|
||||
<li><Link to="/open-source-notices">Public notices</Link></li>
|
||||
<li><Link to={buildSupportPath('operator-access')}>Operator access support</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" className="button button--ghost button--full" onClick={toggleColorMode}>
|
||||
Switch to {colorMode === 'dark' ? 'light' : 'dark'} mode
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,34 @@
|
|||
import { Navigate, Outlet, useLocation } from 'react-router-dom'
|
||||
import { Link, Navigate, Outlet, useLocation } from 'react-router-dom'
|
||||
import { usePlatformAuth } from '../../auth/platform-auth'
|
||||
import { buildLoginRedirectTarget } from '../../auth/route-guard'
|
||||
import { buildSupportPath } from '../../site-routes'
|
||||
import { GeneralPageLoader } from '../ui/Skeletons'
|
||||
|
||||
export function ProtectedRoute() {
|
||||
const { isAuthenticated, isLoading } = usePlatformAuth()
|
||||
const location = useLocation()
|
||||
const loginRedirectTarget = buildLoginRedirectTarget(location)
|
||||
|
||||
if (isLoading) {
|
||||
return <GeneralPageLoader title="Loading operator access..." />
|
||||
return (
|
||||
<GeneralPageLoader
|
||||
title="Loading operator access..."
|
||||
description="Checking your saved browser session, protected release posture, and browser-to-desktop continuity before the operator shell opens."
|
||||
>
|
||||
<div className="button-row general-page-loader-actions">
|
||||
<Link className="button button--ghost" to={loginRedirectTarget}>
|
||||
Open sign-in
|
||||
</Link>
|
||||
<Link className="button button--ghost" to={buildSupportPath('operator-access')}>
|
||||
Open support
|
||||
</Link>
|
||||
</div>
|
||||
</GeneralPageLoader>
|
||||
)
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to={buildLoginRedirectTarget(location)} replace />
|
||||
return <Navigate to={loginRedirectTarget} replace />
|
||||
}
|
||||
|
||||
return <Outlet />
|
||||
|
|
|
|||
66
website/src/components/ui/OperationalStatusCallout.tsx
Normal file
66
website/src/components/ui/OperationalStatusCallout.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { Link } from 'react-router-dom'
|
||||
import { isExternalHref } from '../../site-routes'
|
||||
|
||||
type OperationalStatusSection = {
|
||||
title: string
|
||||
items: readonly string[]
|
||||
}
|
||||
|
||||
type OperationalStatusAction = {
|
||||
label: string
|
||||
to: string
|
||||
}
|
||||
|
||||
export function OperationalStatusCallout({
|
||||
badge,
|
||||
title,
|
||||
summary,
|
||||
sections = [],
|
||||
actions = [],
|
||||
}: {
|
||||
badge: string
|
||||
title: string
|
||||
summary: string
|
||||
sections?: readonly OperationalStatusSection[]
|
||||
actions?: readonly OperationalStatusAction[]
|
||||
}) {
|
||||
const visibleSections = sections.filter((section) => section.items.length > 0)
|
||||
|
||||
return (
|
||||
<div className="callout">
|
||||
<p className="status-pill status-pill--info">{badge}</p>
|
||||
<p><strong>{title}</strong> {summary}</p>
|
||||
{visibleSections.map((section) => (
|
||||
<div key={section.title} className="top-gap">
|
||||
<p><strong>{section.title}</strong></p>
|
||||
<ul className="list top-gap">
|
||||
{section.items.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
{actions.length > 0 ? (
|
||||
<div className="button-row top-gap">
|
||||
{actions.map((action) => (
|
||||
isExternalHref(action.to) ? (
|
||||
<a
|
||||
key={action.label}
|
||||
className="button button--ghost"
|
||||
href={action.to}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{action.label}
|
||||
</a>
|
||||
) : (
|
||||
<Link key={action.label} className="button button--ghost" to={action.to}>
|
||||
{action.label}
|
||||
</Link>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,15 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { type ReactNode, useEffect, useState } from 'react'
|
||||
import { NamedAgentSpinner } from './agent-spinners'
|
||||
|
||||
export function GeneralPageLoader({ title = 'Loading' }: { title?: string }) {
|
||||
export function GeneralPageLoader({
|
||||
title = 'Loading',
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
title?: string
|
||||
description?: string
|
||||
children?: ReactNode
|
||||
}) {
|
||||
const [isCompleting, setIsCompleting] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -30,6 +38,8 @@ export function GeneralPageLoader({ title = 'Loading' }: { title?: string }) {
|
|||
className={`loading-spinner ${isCompleting ? 'is-completing' : ''}`}
|
||||
/>
|
||||
<p className="general-page-loader-text">{title}</p>
|
||||
{description ? <p className="general-page-loader-body">{description}</p> : null}
|
||||
{children ? <div className="general-page-loader-extra">{children}</div> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Link, useSearchParams } from 'react-router-dom'
|
|||
import { buildAuthApiBaseUrls, createDesktopLinkToken, getAuthHealth, getReleaseManifest } 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'
|
||||
|
|
@ -205,6 +206,56 @@ function findWindowsManifestTarget(releaseManifest: ReleaseManifestView) {
|
|||
return releaseManifest.platforms.find((platform) => platform.platform_key === 'windows') || null
|
||||
}
|
||||
|
||||
const protectedPackagedDesktopProofTitle = 'Current packaged desktop proof'
|
||||
|
||||
function ProtectedReleaseAuthorityNotice({
|
||||
badge,
|
||||
title,
|
||||
summary,
|
||||
stillWorks,
|
||||
actionTo,
|
||||
actionLabel,
|
||||
}: {
|
||||
badge: string
|
||||
title: string
|
||||
summary: string
|
||||
stillWorks: readonly string[]
|
||||
actionTo: string
|
||||
actionLabel: string
|
||||
}) {
|
||||
return (
|
||||
<OperationalStatusCallout
|
||||
badge={badge}
|
||||
title={title}
|
||||
summary={summary}
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: stillWorks,
|
||||
},
|
||||
{
|
||||
title: 'What stays intentionally withheld',
|
||||
items: [
|
||||
'Direct package delivery remains intentionally withheld until the auth server returns.',
|
||||
'Live server-backed release authority is not claimed while this protected lane is in fallback posture.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
'Wait for the auth server to recover, then refresh this protected lane before treating release metadata as live authority.',
|
||||
'Use operator support if fallback posture persists while rollout or entitlement work is blocked.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
actions={[
|
||||
{ label: actionLabel, to: actionTo },
|
||||
{ label: 'Open support', to: buildSupportPath('operator-access') },
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function PackagedValidationPanel({
|
||||
title,
|
||||
kicker,
|
||||
|
|
@ -415,7 +466,35 @@ export function DashboardOverviewPage() {
|
|||
|
||||
<Panel title="Auth and server health" kicker="Browser shell">
|
||||
{healthQuery.isLoading ? <p>Checking auth server health...</p> : null}
|
||||
{healthQuery.isError ? <p className="form-error">Auth health probe failed. The local fallback can still keep the dashboard usable.</p> : null}
|
||||
{healthQuery.isError ? (
|
||||
<OperationalStatusCallout
|
||||
badge="Auth probe degraded"
|
||||
title="Auth health probe failed."
|
||||
summary="The local fallback can still keep the dashboard usable."
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: [
|
||||
'Local session, packaged proof, and browser-to-desktop rollout guidance remain visible.',
|
||||
'Protected dashboard orientation and support escalation surfaces remain available.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'What stays intentionally bounded',
|
||||
items: [
|
||||
'Do not treat browser auth as shared production authority until the live auth probe recovers.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
'Restore shared auth reachability before trusting live entitlement or desktop-link issuance.',
|
||||
'Use operator support if the protected dashboard stays in degraded auth posture.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{healthQuery.data ? (
|
||||
<ul className="list">
|
||||
<li>Service: {healthQuery.data.service}</li>
|
||||
|
|
@ -432,9 +511,32 @@ export function DashboardOverviewPage() {
|
|||
</ul>
|
||||
) : null}
|
||||
{healthQuery.data?.fallback.active ? (
|
||||
<p className="form-error">
|
||||
Shared auth core is not fully ready right now. Dashboard fallback posture remains available{healthQuery.data.fallback.reason ? ` (${healthQuery.data.fallback.reason})` : ''}.
|
||||
</p>
|
||||
<OperationalStatusCallout
|
||||
badge="Dashboard fallback posture"
|
||||
title="Shared auth core is not fully ready right now."
|
||||
summary={`Dashboard fallback posture remains available${healthQuery.data.fallback.reason ? ` (${healthQuery.data.fallback.reason})` : ''}.`}
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: [
|
||||
'Bounded session continuity, packaged proof, and operator guidance remain usable inside the protected dashboard.',
|
||||
'Account, release, and desktop-pairing surfaces can still be reviewed without claiming live shared-auth authority.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'What stays intentionally bounded',
|
||||
items: [
|
||||
'Live entitlement proof, shared-production session authority, and rollout trust should wait for the auth core to recover.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
'Clear auth-core readiness problems first, then re-check release authority and desktop-link issuance.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{healthQuery.data && (healthQuery.data.runtime.errors.length > 0 || healthQuery.data.runtime.warnings.length > 0) ? (
|
||||
<div className="callout">
|
||||
|
|
@ -507,9 +609,17 @@ export function DashboardOverviewPage() {
|
|||
<p>Refreshing release-manifest download readiness from the auth server.</p>
|
||||
) : null}
|
||||
{releaseManifestQuery.isError ? (
|
||||
<p className="form-error">
|
||||
Live release-manifest lookup failed. Download readiness is currently using local fallback metadata rather than current runtime release authority.
|
||||
</p>
|
||||
<ProtectedReleaseAuthorityNotice
|
||||
badge="Dashboard release fallback"
|
||||
title="Live release-manifest lookup failed."
|
||||
summary="Download readiness is currently using local fallback metadata rather than current runtime release authority."
|
||||
stillWorks={[
|
||||
'Protected launch-readiness interpretation, packaged proof, and account posture remain visible.',
|
||||
'The operator dashboard can still separate auth, package, runtime, and rollout work while release authority recovers.',
|
||||
]}
|
||||
actionTo="/app/downloads"
|
||||
actionLabel="Open downloads"
|
||||
/>
|
||||
) : null}
|
||||
{launchReadinessIssues.length > 0 ? (
|
||||
<p className="form-error">
|
||||
|
|
@ -601,10 +711,17 @@ export function DownloadCenterPage() {
|
|||
|
||||
{releaseManifestQuery.isError ? (
|
||||
<Panel title="Release manifest status" kicker="Runtime authority">
|
||||
<p className="form-error">
|
||||
The live release manifest could not be loaded from the auth server right now.
|
||||
This panel is showing bounded fallback site metadata instead of current runtime release authority, and direct package delivery remains intentionally withheld until the auth server returns.
|
||||
</p>
|
||||
<ProtectedReleaseAuthorityNotice
|
||||
badge="Protected release fallback"
|
||||
title="The live release manifest could not be loaded from the auth server right now."
|
||||
summary="This panel is showing bounded fallback site metadata instead of current runtime release authority, and direct package delivery remains intentionally withheld until the auth server returns."
|
||||
stillWorks={[
|
||||
'Requested platform targeting, packaged proof, and protected rollout guidance remain visible.',
|
||||
'Signed-in entitlement posture can still be reviewed without pretending the missing manifest is live authority.',
|
||||
]}
|
||||
actionTo="/app"
|
||||
actionLabel="Open dashboard"
|
||||
/>
|
||||
</Panel>
|
||||
) : null}
|
||||
|
||||
|
|
@ -683,34 +800,42 @@ export function DownloadCenterPage() {
|
|||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
{(releaseManifest.public_docs_url || releaseManifest.release_notes_url || releaseManifest.corresponding_source_url) ? (
|
||||
<div className="button-row top-gap">
|
||||
{releaseManifest.public_docs_url ? (
|
||||
<a className="button button--ghost" href={releaseManifest.public_docs_url} target="_blank" rel="noreferrer">
|
||||
Public docs
|
||||
</a>
|
||||
) : null}
|
||||
{releaseManifest.release_notes_url ? (
|
||||
<a className="button button--ghost" href={releaseManifest.release_notes_url} target="_blank" rel="noreferrer">
|
||||
Release notes
|
||||
</a>
|
||||
) : null}
|
||||
<a
|
||||
className="button button--ghost"
|
||||
href={releaseManifest.corresponding_source_url || '/open-source-notices'}
|
||||
target={releaseManifest.corresponding_source_url ? '_blank' : undefined}
|
||||
rel={releaseManifest.corresponding_source_url ? 'noreferrer' : undefined}
|
||||
>
|
||||
{releaseManifest.corresponding_source_url ? 'Corresponding source' : 'Open source notices'}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
<ReleaseAuthorityLinks
|
||||
releaseManifest={releaseManifest}
|
||||
includePublicNoticesLink
|
||||
includeProtectedNoticesLink
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<PackagedValidationPanel
|
||||
title={protectedPackagedDesktopProofTitle}
|
||||
kicker="Protected release evidence"
|
||||
description="This protected download center now mirrors the same Windows packaged-validation proof used across the public and protected release surfaces so entitled package access stays tied to real native evidence."
|
||||
emptyMessage="The protected download center could not find a Windows release target, so packaged desktop proof is unavailable here right now."
|
||||
releaseManifest={releaseManifest}
|
||||
/>
|
||||
|
||||
<DesktopFirstLaunchChecklist
|
||||
title="First launch follow-through"
|
||||
kicker="Desktop setup"
|
||||
/>
|
||||
|
||||
<SupportEscalationChecklist
|
||||
title="Download and rollout escalation"
|
||||
kicker="Keep account, package, runtime, and compliance issues separated"
|
||||
description="Use the same bounded escalation map here so signed-in operators can distinguish entitlement trouble, package trouble, runtime regressions, and rollout/compliance work before escalating."
|
||||
secondaryLink={{ to: '/app/notices', label: 'Review notices' }}
|
||||
/>
|
||||
|
||||
<Panel title="Current product-surface map" kicker="Protected operator orientation">
|
||||
<p>
|
||||
The entitled download lane is only one part of the product. Keep the browser dashboard,
|
||||
embedded browser shell, native runtime, and still-frozen browser branch separated so release actions do not drift into the wrong surface.
|
||||
</p>
|
||||
<div className="top-gap">
|
||||
<ProductSurfaceMatrix rows={productSurfaceMatrixRows} />
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
|
@ -740,10 +865,27 @@ export function BrowserAccessPage() {
|
|||
<li>The public browser dashboard is for operator/account/release access, not a claim of full simulator parity.</li>
|
||||
</ul>
|
||||
{healthQuery.isLoading ? <p>Checking current browser-shell auth posture...</p> : null}
|
||||
{healthQuery.isError ? (
|
||||
<p className="form-error">
|
||||
Runtime auth-health could not be loaded. The page is still using the bounded dashboard posture rather than claiming browser-simulator parity.
|
||||
</p>
|
||||
{healthQuery.isError ? (
|
||||
<OperationalStatusCallout
|
||||
badge="Browser access fallback"
|
||||
title="Runtime auth-health could not be loaded."
|
||||
summary="The page is still using the bounded dashboard posture rather than claiming browser-simulator parity."
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: [
|
||||
'Browser-to-desktop handoff guidance and native packaged proof remain visible.',
|
||||
'The signed-in browser shell can still orient the operator without claiming simulator parity.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
'Restore auth-health reachability before treating this route as live browser-session authority.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{healthQuery.data ? (
|
||||
<ul className="list top-gap">
|
||||
|
|
@ -779,10 +921,18 @@ export function BrowserAccessPage() {
|
|||
description="This protected browser route keeps the simulator handoff honest by showing what the native runtime already owns today and where the later XR/controller completion packet still begins."
|
||||
/>
|
||||
<Panel title="Release authority around the browser shell" kicker="Distribution context">
|
||||
{releaseManifestQuery.isError ? (
|
||||
<p className="form-error">
|
||||
Live release-manifest lookup failed. Browser-shell release context is currently using fallback metadata instead of current auth-server authority.
|
||||
</p>
|
||||
{releaseManifestQuery.isError ? (
|
||||
<ProtectedReleaseAuthorityNotice
|
||||
badge="Browser-access release fallback"
|
||||
title="Live release-manifest lookup failed."
|
||||
summary="Browser-shell release context is currently using fallback metadata instead of current auth-server authority."
|
||||
stillWorks={[
|
||||
'Browser-to-desktop handoff guidance, packaged proof, and support references remain available.',
|
||||
'This route can still show the bounded browser-shell role without claiming live release authority.',
|
||||
]}
|
||||
actionTo="/app"
|
||||
actionLabel="Open dashboard"
|
||||
/>
|
||||
) : null}
|
||||
<ul className="list">
|
||||
<li>Configured release targets: {configuredPlatformCount}/{releaseManifest.platforms.length}</li>
|
||||
|
|
@ -792,7 +942,7 @@ export function BrowserAccessPage() {
|
|||
<ReleaseAuthorityLinks releaseManifest={releaseManifest} includeProtectedNoticesLink />
|
||||
</Panel>
|
||||
<PackagedValidationPanel
|
||||
title="Current packaged desktop proof"
|
||||
title={protectedPackagedDesktopProofTitle}
|
||||
kicker="Browser-to-desktop release evidence"
|
||||
description="This protected browser route mirrors the current Windows package proof so account access, pairing, and browser-to-desktop handoff stay anchored to the real native release lane instead of sounding like a browser-only simulator claim."
|
||||
emptyMessage="The current protected browser route could not find a Windows release target, so native packaged-validation proof is unavailable here right now."
|
||||
|
|
@ -870,10 +1020,18 @@ export function AccountPage() {
|
|||
</Panel>
|
||||
<Panel title="Entitlement and release access" kicker="Protected release lane">
|
||||
{releaseManifestQuery.isLoading ? <p>Refreshing release-access posture from the live manifest...</p> : null}
|
||||
{releaseManifestQuery.isError ? (
|
||||
<p className="form-error">
|
||||
Live release-manifest lookup failed. This account view is using bounded fallback metadata until auth-server release authority returns.
|
||||
</p>
|
||||
{releaseManifestQuery.isError ? (
|
||||
<ProtectedReleaseAuthorityNotice
|
||||
badge="Account release fallback"
|
||||
title="Live release-manifest lookup failed."
|
||||
summary="This account view is using bounded fallback metadata until auth-server release authority returns."
|
||||
stillWorks={[
|
||||
'Signed-in profile, entitlement posture, and packaged proof remain visible.',
|
||||
'Protected follow-through links still keep account, download, notices, and support separated.',
|
||||
]}
|
||||
actionTo="/app"
|
||||
actionLabel="Open dashboard"
|
||||
/>
|
||||
) : null}
|
||||
<ul className="list">
|
||||
<li>Manifest viewer authenticated: {releaseManifest.viewer.authenticated ? 'yes' : 'no'}</li>
|
||||
|
|
@ -896,7 +1054,7 @@ export function AccountPage() {
|
|||
) : null}
|
||||
</Panel>
|
||||
<PackagedValidationPanel
|
||||
title="Current packaged desktop proof"
|
||||
title={protectedPackagedDesktopProofTitle}
|
||||
kicker="Release evidence"
|
||||
description="Entitlement and manifest access stay more trustworthy when the same protected account route also exposes the current Windows packaged proof for the higher-dimensional desktop runtime."
|
||||
emptyMessage="The current protected account route could not find a Windows release target, so packaged desktop evidence is unavailable here right now."
|
||||
|
|
@ -959,9 +1117,17 @@ export function NoticesPage() {
|
|||
</p>
|
||||
{releaseManifestQuery.isLoading ? <p>Refreshing notices posture from the live release manifest...</p> : null}
|
||||
{releaseManifestQuery.isError ? (
|
||||
<p className="form-error">
|
||||
Live release-manifest lookup failed. This page is using bounded fallback notice metadata until auth-server release authority returns.
|
||||
</p>
|
||||
<ProtectedReleaseAuthorityNotice
|
||||
badge="Notices fallback"
|
||||
title="Live release-manifest lookup failed."
|
||||
summary="This page is using bounded fallback notice metadata until auth-server release authority returns."
|
||||
stillWorks={[
|
||||
'Protected notice references, corresponding-source posture, and packaged proof remain visible.',
|
||||
'Operators can still keep distribution work separate from runtime and entitlement work while authority recovers.',
|
||||
]}
|
||||
actionTo="/app/downloads"
|
||||
actionLabel="Open downloads"
|
||||
/>
|
||||
) : null}
|
||||
<ul className="list top-gap">
|
||||
<li>Configured release targets: {configuredPlatforms.length}/{releaseManifest.platforms.length}</li>
|
||||
|
|
@ -987,10 +1153,10 @@ export function NoticesPage() {
|
|||
) : null}
|
||||
</Panel>
|
||||
<PackagedValidationPanel
|
||||
title="Current packaged release proof"
|
||||
title={protectedPackagedDesktopProofTitle}
|
||||
kicker="Distribution evidence"
|
||||
description="This protected notices lane now keeps the downloadable release story tied to the current Windows package proof instead of only naming validation abstractly. That makes notices, corresponding source, and package posture easier to read together."
|
||||
emptyMessage="The current protected notices route could not find a Windows release target, so packaged release evidence is unavailable here right now."
|
||||
description="This protected notices lane now keeps the downloadable release story tied to the current Windows desktop package proof instead of only naming validation abstractly. That makes notices, corresponding source, and package posture easier to read together."
|
||||
emptyMessage="The current protected notices route could not find a Windows release target, so packaged desktop evidence is unavailable here right now."
|
||||
releaseManifest={releaseManifest}
|
||||
/>
|
||||
<Panel title="Reference surfaces" kicker="Release references">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import {
|
|||
isOrcidOAuthEnabled,
|
||||
} from '../auth/supertokens-client'
|
||||
import { SiteMetadata } from '../components/seo/SiteMetadata'
|
||||
import { OperationalStatusCallout } from '../components/ui/OperationalStatusCallout'
|
||||
import { deliverySurfaceCards, operatorManualTracks } from '../site-data'
|
||||
import { buildSupportPath, getDownloadPlatformLabel, normalizeDownloadPlatform } from '../site-routes'
|
||||
|
||||
function AuthShell({
|
||||
title,
|
||||
|
|
@ -39,33 +42,185 @@ function useNextPath() {
|
|||
return normalizeNextPath(searchParams.get('next'))
|
||||
}
|
||||
|
||||
type AuthNextStepDescriptor = {
|
||||
title: string
|
||||
description: string
|
||||
bullets: readonly string[]
|
||||
}
|
||||
|
||||
function resolveAuthNextStep(nextPath: string): AuthNextStepDescriptor {
|
||||
const [pathname, rawQuery = ''] = nextPath.split('?')
|
||||
const searchParams = new URLSearchParams(rawQuery)
|
||||
|
||||
if (pathname === '/app/downloads') {
|
||||
const platform = normalizeDownloadPlatform(searchParams.get('platform'))
|
||||
const platformLabel = platform ? getDownloadPlatformLabel(platform) : 'desktop'
|
||||
|
||||
return {
|
||||
title: `Protected ${platformLabel} release lane`,
|
||||
description:
|
||||
`After sign-in you will continue to the protected ${platformLabel} download center so entitlement, current package proof, and release-notice follow-through stay tied to your account instead of being exposed on an anonymous page.`,
|
||||
bullets: [
|
||||
`Review the current ${platformLabel} target, package proof, and release notes before download.`,
|
||||
'Use the protected dashboard afterward if you need a desktop-link token for first launch.',
|
||||
'Return to protected or public notices if rollout or compliance posture changes.',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (pathname === '/app/account') {
|
||||
return {
|
||||
title: 'Protected account lane',
|
||||
description:
|
||||
'After sign-in you will continue to the protected account surface so session state, plan posture, and browser-to-desktop access continuity can be confirmed before any download or rollout step.',
|
||||
bullets: [
|
||||
'Confirm your plan, role, and current release access first.',
|
||||
'Use the protected download center once desktop entitlement is resolved.',
|
||||
'Use the dashboard and notices lanes if account or release posture changes later.',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (pathname === '/app/notices') {
|
||||
return {
|
||||
title: 'Protected notices lane',
|
||||
description:
|
||||
'After sign-in you will continue to the protected notices surface so distribution references, corresponding-source posture, and release-target notice truth remain attached to the signed-in operator lane.',
|
||||
bullets: [
|
||||
'Review notices before redistributing any covered downloadable build.',
|
||||
'Use the protected download center when you need the entitled package itself.',
|
||||
'Keep browser access, native package proof, and legal posture synchronized.',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: 'Protected operator dashboard',
|
||||
description:
|
||||
'After sign-in you will continue to the protected operator dashboard so browser auth, billing, release posture, and desktop-link pairing are resolved before the native simulator lane takes over.',
|
||||
bullets: [
|
||||
'Confirm account, plan, and release readiness posture first.',
|
||||
'Open the protected download center when you need the entitled desktop build.',
|
||||
'Generate a desktop-link token there or from the dashboard before first native launch.',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
function AuthNextStepNotice({ nextPath }: { nextPath: string }) {
|
||||
const nextStep = resolveAuthNextStep(nextPath)
|
||||
|
||||
return (
|
||||
<div className="callout">
|
||||
<p className="status-pill status-pill--info">After sign-in</p>
|
||||
<p><strong>{nextStep.title}.</strong> {nextStep.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{nextStep.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AuthAccessGuide({ nextPath }: { nextPath: string }) {
|
||||
const nextStep = resolveAuthNextStep(nextPath)
|
||||
const dashboardCard = deliverySurfaceCards.find((card) => card.title === 'Protected browser dashboard')
|
||||
const runtimeCard = deliverySurfaceCards.find((card) => card.title === 'Native Unreal desktop runtime')
|
||||
const pairingTrack = operatorManualTracks.find((track) => track.title === '3. Pair the installed desktop runtime')
|
||||
|
||||
return (
|
||||
<div className="card-grid top-gap">
|
||||
<article className="card card--compact">
|
||||
<h3>{nextStep.title}</h3>
|
||||
<p>{nextStep.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{nextStep.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
{dashboardCard ? (
|
||||
<article className="card card--compact">
|
||||
<h3>{dashboardCard.title}</h3>
|
||||
<p>{dashboardCard.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{dashboardCard.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
) : null}
|
||||
{runtimeCard && pairingTrack ? (
|
||||
<article className="card card--compact">
|
||||
<h3>{runtimeCard.title}</h3>
|
||||
<p>{pairingTrack.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{pairingTrack.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AuthRuntimeNotice() {
|
||||
const authRuntimeValidation = getSuperTokensAuthRuntimeValidation()
|
||||
if (authRuntimeValidation.ready && authRuntimeValidation.warnings.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const diagnostics = [
|
||||
...authRuntimeValidation.missing.map((item) => `Missing auth env: ${item}`),
|
||||
...authRuntimeValidation.warnings,
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="callout">
|
||||
<p>
|
||||
Shared browser auth is not fully in production posture yet.
|
||||
{!authRuntimeValidation.ready ? ' Local fallback mode may activate until the required auth env vars are configured.' : ''}
|
||||
</p>
|
||||
{authRuntimeValidation.missing.length > 0 ? (
|
||||
<ul className="list">
|
||||
{authRuntimeValidation.missing.map((item) => (
|
||||
<li key={item}>Missing auth env: {item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
{authRuntimeValidation.warnings.length > 0 ? (
|
||||
<ul className="list">
|
||||
{authRuntimeValidation.warnings.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
<OperationalStatusCallout
|
||||
badge={authRuntimeValidation.ready ? 'Auth launch warning' : 'Auth fallback posture'}
|
||||
title={
|
||||
authRuntimeValidation.ready
|
||||
? 'Shared browser auth is present, but launch posture is still mixed.'
|
||||
: 'Shared browser auth is not fully in production posture yet.'
|
||||
}
|
||||
summary={
|
||||
authRuntimeValidation.ready
|
||||
? 'Browser sign-in can still continue, but launch-facing auth warnings should be cleared before this surface is treated as fully production-ready.'
|
||||
: 'Local fallback mode may activate until the required auth env vars are configured.'
|
||||
}
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: [
|
||||
'Login, registration, and safe next-step routing remain available.',
|
||||
'The protected dashboard can still recover bounded account, release, and desktop-pairing posture after sign-in.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'What stays intentionally bounded',
|
||||
items: [
|
||||
'Shared production auth, live entitlement authority, and deployment-grade session guarantees are not claimed until this auth surface is fully green.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Runtime diagnostics',
|
||||
items: diagnostics,
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
authRuntimeValidation.ready
|
||||
? 'Clear the remaining mixed-mode warnings before public launch so browser auth, protected release access, and account state share one production authority.'
|
||||
: 'Configure the missing auth runtime values first, then re-check non-loopback HTTPS origins before public launch.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
actions={[
|
||||
{ label: 'Open support', to: buildSupportPath('operator-access') },
|
||||
{ label: 'Review download posture', to: '/download' },
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +266,7 @@ export function LoginPage() {
|
|||
footer={<p>Need access? <Link to={`/register?next=${encodeURIComponent(nextPath)}`}>Create an account</Link>.</p>}
|
||||
>
|
||||
<AuthRuntimeNotice />
|
||||
<AuthNextStepNotice nextPath={nextPath} />
|
||||
<form className="auth-form" onSubmit={handleSubmit}>
|
||||
<label className="input-label" htmlFor="login-email">Email</label>
|
||||
<input id="login-email" className="input" value={email} onChange={(event) => setEmail(event.target.value)} />
|
||||
|
|
@ -138,6 +294,7 @@ export function LoginPage() {
|
|||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<AuthAccessGuide nextPath={nextPath} />
|
||||
</AuthShell>
|
||||
</>
|
||||
)
|
||||
|
|
@ -186,6 +343,7 @@ export function RegisterPage() {
|
|||
footer={<p>Already have access? <Link to={`/login?next=${encodeURIComponent(nextPath)}`}>Log in</Link>.</p>}
|
||||
>
|
||||
<AuthRuntimeNotice />
|
||||
<AuthNextStepNotice nextPath={nextPath} />
|
||||
<form className="auth-form" onSubmit={handleSubmit}>
|
||||
<label className="input-label" htmlFor="register-name">Name</label>
|
||||
<input id="register-name" className="input" value={name} onChange={(event) => setName(event.target.value)} />
|
||||
|
|
@ -198,6 +356,7 @@ export function RegisterPage() {
|
|||
{isSubmitting ? 'Creating account...' : 'Create account'}
|
||||
</button>
|
||||
</form>
|
||||
<AuthAccessGuide nextPath={nextPath} />
|
||||
</AuthShell>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { Link } from 'react-router-dom'
|
|||
import { getReleaseManifest } from '../auth/auth-api'
|
||||
import { MarketingShell } from '../components/layout/MarketingShell'
|
||||
import { SiteMetadata } from '../components/seo/SiteMetadata'
|
||||
import { OperationalStatusCallout } from '../components/ui/OperationalStatusCallout'
|
||||
import { ProductSurfaceMatrix } from '../components/ui/ProductSurfaceMatrix'
|
||||
import { PublicLaunchStatus } from '../components/ui/PublicLaunchStatus'
|
||||
import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary'
|
||||
import { paddleReadyDescription } from '../site-config'
|
||||
|
|
@ -22,6 +24,9 @@ import {
|
|||
openSourceNotices,
|
||||
operatorManualTracks,
|
||||
privacyBoundaryCards,
|
||||
productSurfaceMatrixRows,
|
||||
releaseRolloutChecklist,
|
||||
supportEscalationCards,
|
||||
termsBoundaryCards,
|
||||
} from '../site-data'
|
||||
import {
|
||||
|
|
@ -43,10 +48,18 @@ export function PricingPage() {
|
|||
queryFn: getReleaseManifest,
|
||||
retry: false,
|
||||
})
|
||||
const releaseManifest = useMemo(
|
||||
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
|
||||
[releaseManifestQuery.data?.manifest],
|
||||
)
|
||||
const releaseCommerce = useMemo(
|
||||
() => resolveReleaseCommerceView(releaseManifestQuery.data?.manifest, releaseCommerceFallback),
|
||||
[releaseManifestQuery.data?.manifest],
|
||||
)
|
||||
const windowsValidationPlatform = useMemo(
|
||||
() => releaseManifest.platforms.find((platform) => platform.platform_key === 'windows') || null,
|
||||
[releaseManifest.platforms],
|
||||
)
|
||||
const runtimePlanCatalog = useMemo(() => ([
|
||||
explorerFallbackPlan,
|
||||
{
|
||||
|
|
@ -97,6 +110,14 @@ export function PricingPage() {
|
|||
<PublicLaunchStatus />
|
||||
</Section>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
actions={[
|
||||
{ to: '/download', label: 'Open download center' },
|
||||
{ to: '/open-source-notices', label: 'Review notices' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="What happens after access is granted"
|
||||
description="Pricing only stays professional when it explains the real path from browser entitlement into the packaged simulator instead of stopping at the checkout button."
|
||||
|
|
@ -123,6 +144,44 @@ export function PricingPage() {
|
|||
<DeliverySurfaceResponsibilitiesGrid limit={3} />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Commercial distribution doctrine"
|
||||
description="Commercial pages should stay as explicit about release and legal posture as the rest of the public site."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{distributionDoctrineCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Terms of access in practice"
|
||||
description="Pricing is easier to trust when the access model, simulator boundary, and operator obligations stay visible before checkout."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{termsBoundaryCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Important launch note">
|
||||
<article className="callout">
|
||||
<p>
|
||||
|
|
@ -147,6 +206,10 @@ export function DownloadPage() {
|
|||
() => resolveReleaseManifestView(releaseManifestQuery.data?.manifest, buildPublicReleaseManifestFallback()),
|
||||
[releaseManifestQuery.data?.manifest],
|
||||
)
|
||||
const windowsValidationPlatform = useMemo(
|
||||
() => releaseManifest.platforms.find((platform) => platform.platform_key === 'windows') || null,
|
||||
[releaseManifest.platforms],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -170,12 +233,38 @@ export function DownloadPage() {
|
|||
|
||||
{releaseManifestQuery.isError ? (
|
||||
<Section title="Release manifest status">
|
||||
<article className="callout">
|
||||
<p>
|
||||
The live release manifest could not be loaded from the auth server right now.
|
||||
This page is showing bounded fallback site metadata instead of current runtime release authority, and direct package delivery stays intentionally withheld until that authority returns.
|
||||
</p>
|
||||
</article>
|
||||
<OperationalStatusCallout
|
||||
badge="Public release fallback"
|
||||
title="The live release manifest could not be loaded from the auth server right now."
|
||||
summary="This page is showing bounded fallback site metadata instead of current runtime release authority, and direct package delivery stays intentionally withheld until that authority returns."
|
||||
sections={[
|
||||
{
|
||||
title: 'What still works',
|
||||
items: [
|
||||
'Supported targets, packaged proof, and public rollout guidance remain visible.',
|
||||
'Platform selection can still be preserved into the protected sign-in and download handoff.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'What stays intentionally withheld',
|
||||
items: [
|
||||
'Raw desktop delivery URLs remain behind the protected release lane.',
|
||||
'This public page does not claim live entitled release authority while the auth server is unavailable.',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Recommended recovery order',
|
||||
items: [
|
||||
'Use the protected dashboard once the auth server recovers if you need actual package delivery.',
|
||||
'Open operator support if release-authority fallback persists during rollout or purchase work.',
|
||||
],
|
||||
},
|
||||
]}
|
||||
actions={[
|
||||
{ label: 'Sign in to inspect access', to: '/login' },
|
||||
{ label: 'Open support', to: buildSupportPath('operator-access') },
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
|
|
@ -247,6 +336,33 @@ export function DownloadPage() {
|
|||
<PublicLaunchStatus title="Desktop release access stays launch-honest" />
|
||||
</Section>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
actions={[
|
||||
{ to: '/changelog', label: 'Review release notes' },
|
||||
{ to: '/open-source-notices', label: 'Review notices' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Digital delivery workflow"
|
||||
description="The download page should explain the real delivery sequence from platform selection to protected entitlement and first desktop launch."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{digitalDeliveryCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Browser and desktop responsibilities"
|
||||
description="The release lane is easier to trust when the site explains why some actions stay public, some stay protected, and the simulator itself stays native."
|
||||
|
|
@ -254,6 +370,25 @@ export function DownloadPage() {
|
|||
<DeliverySurfaceResponsibilitiesGrid />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Distribution doctrine"
|
||||
description="Download posture should stay tied to package proof, notices, and the desktop-first simulator boundary."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{distributionDoctrineCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Release integrity and documentation">
|
||||
<div className="card-grid">
|
||||
{desktopReleaseSignals.map((signal) => (
|
||||
|
|
@ -421,12 +556,32 @@ export function OpenSourceNoticesPage() {
|
|||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Release and redistribution checklist"
|
||||
description="Legal pages stay more useful when they explain the concrete release follow-through that should happen before broader redistribution."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{releaseRolloutChecklist.map((card) => (
|
||||
<article className="card" key={card.title}>
|
||||
<h3>{card.title}</h3>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</MarketingShell>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function PrivacyPage() {
|
||||
const { windowsValidationPlatform } = usePublicReleaseManifestView('public-privacy')
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteMetadata
|
||||
|
|
@ -467,12 +622,55 @@ export function PrivacyPage() {
|
|||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Browser and desktop responsibilities"
|
||||
description="Privacy language stays honest when it says plainly which surface owns public identity work and which surface owns simulator execution."
|
||||
>
|
||||
<DeliverySurfaceResponsibilitiesGrid limit={3} />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Current surface authority map"
|
||||
description="This keeps privacy expectations grounded in the live product topology instead of a vague all-in-one app claim."
|
||||
>
|
||||
<ProductSurfaceMatrix rows={productSurfaceMatrixRows} />
|
||||
</Section>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
actions={[
|
||||
{ to: '/download', label: 'Open download center' },
|
||||
{ to: '/support', label: 'Open support' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Support-safe escalation boundary"
|
||||
description="When privacy or access concerns show up during rollout, support should keep account, runtime, and compliance questions separated instead of flattening them together."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{supportEscalationCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</MarketingShell>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function TermsPage() {
|
||||
const { windowsValidationPlatform } = usePublicReleaseManifestView('public-terms')
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteMetadata
|
||||
|
|
@ -513,6 +711,46 @@ export function TermsPage() {
|
|||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Browser and desktop responsibilities"
|
||||
description="Terms stay more accurate when they keep public site duties, protected dashboard duties, and native simulator duties separated."
|
||||
>
|
||||
<DeliverySurfaceResponsibilitiesGrid />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Current surface authority map"
|
||||
description="This is the quickest way to see which live surface owns access, execution, and future-gated branches today."
|
||||
>
|
||||
<ProductSurfaceMatrix rows={productSurfaceMatrixRows} />
|
||||
</Section>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
actions={[
|
||||
{ to: '/download', label: 'Open download center' },
|
||||
{ to: '/open-source-notices', label: 'Review notices' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Release and redistribution checklist"
|
||||
description="Terms become materially more trustworthy when they stay attached to release proof, download gating, and legal follow-through."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{releaseRolloutChecklist.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</MarketingShell>
|
||||
</>
|
||||
)
|
||||
|
|
@ -563,6 +801,32 @@ export function ShippingPaymentPage() {
|
|||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="What happens after access is granted"
|
||||
description="Payment surfaces are stronger when they explain the next real operator steps instead of stopping at checkout language."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{operatorManualTracks.slice(1, 4).map((track) => (
|
||||
<article key={track.title} className="card">
|
||||
<h3>{track.title}</h3>
|
||||
<p>{track.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{track.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Browser and desktop responsibilities"
|
||||
description="Shipping and payment surfaces should explain why checkout, entitlement, and release posture stay on the web while simulator execution stays native."
|
||||
>
|
||||
<DeliverySurfaceResponsibilitiesGrid />
|
||||
</Section>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
actions={[
|
||||
|
|
@ -570,6 +834,62 @@ export function ShippingPaymentPage() {
|
|||
{ to: '/open-source-notices', label: 'Review notices' },
|
||||
]}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Terms of access in practice"
|
||||
description="The shipping/payment page should still tell operators what they are actually buying access to and what remains outside the current browser scope."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{termsBoundaryCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Distribution doctrine"
|
||||
description="This keeps shipping/payment language attached to package proof, notices, and the desktop-first runtime truth."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{distributionDoctrineCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Release and redistribution checklist"
|
||||
description="Digital delivery is only complete when payment, download, proof, and legal follow-through stay aligned."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{releaseRolloutChecklist.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</MarketingShell>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import {
|
|||
changelogEntries,
|
||||
companyNarrative,
|
||||
deploymentReadinessTracks,
|
||||
digitalDeliveryCards,
|
||||
featureAtlasCurrentTracks,
|
||||
heroMetrics,
|
||||
higherDimensionalRuntimeGuideCards,
|
||||
controlProfileRosterCards,
|
||||
|
|
@ -25,6 +27,7 @@ import {
|
|||
roadmapHonestyCards,
|
||||
runtimeControlGuideCards,
|
||||
shippingNowCards,
|
||||
privacyBoundaryCards,
|
||||
supportEscalationCards,
|
||||
simulatorManualCards,
|
||||
supportFaqs,
|
||||
|
|
@ -37,6 +40,162 @@ import {
|
|||
usePublicReleaseManifestView,
|
||||
} from './public-page-helpers'
|
||||
|
||||
type PrincipleCard = {
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
type BulletCard = {
|
||||
title: string
|
||||
description: string
|
||||
bullets: readonly string[]
|
||||
}
|
||||
|
||||
type StepCard = {
|
||||
title: string
|
||||
description: string
|
||||
steps: readonly string[]
|
||||
}
|
||||
|
||||
type StepOnlyCard = {
|
||||
title: string
|
||||
steps: readonly string[]
|
||||
}
|
||||
|
||||
type FaqCard = {
|
||||
question: string
|
||||
answer: string
|
||||
}
|
||||
|
||||
function PrincipleCardSection({
|
||||
title,
|
||||
description,
|
||||
cards,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
cards: readonly PrincipleCard[]
|
||||
}) {
|
||||
return (
|
||||
<Section title={title} description={description}>
|
||||
<div className="card-grid">
|
||||
{cards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function BulletCardSection({
|
||||
title,
|
||||
description,
|
||||
cards,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
cards: readonly BulletCard[]
|
||||
}) {
|
||||
return (
|
||||
<Section title={title} description={description}>
|
||||
<div className="card-grid">
|
||||
{cards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function StepCardSection({
|
||||
title,
|
||||
description,
|
||||
cards,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
cards: readonly StepCard[]
|
||||
}) {
|
||||
return (
|
||||
<Section title={title} description={description}>
|
||||
<div className="card-grid">
|
||||
{cards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function StepOnlyCardSection({
|
||||
title,
|
||||
description,
|
||||
cards,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
cards: readonly StepOnlyCard[]
|
||||
}) {
|
||||
return (
|
||||
<Section title={title} description={description}>
|
||||
<div className="card-grid">
|
||||
{cards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<ul className="list top-gap">
|
||||
{card.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function FaqCardSection({
|
||||
title,
|
||||
description,
|
||||
cards,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
cards: readonly FaqCard[]
|
||||
}) {
|
||||
return (
|
||||
<Section title={title} description={description}>
|
||||
<div className="card-grid">
|
||||
{cards.map((card) => (
|
||||
<article key={card.question} className="card">
|
||||
<h3>{card.question}</h3>
|
||||
<p>{card.answer}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
export function HomeLanding() {
|
||||
const { windowsValidationPlatform } = usePublicReleaseManifestView('public-home')
|
||||
|
||||
|
|
@ -573,35 +732,19 @@ export function DocsPage() {
|
|||
title="HyperTwist documentation stays capability-accurate."
|
||||
lede="The public docs surface points users to product-safe truth: feature registry discipline, roadmap honesty, release notes, and distribution/legal guidance."
|
||||
>
|
||||
<Section title="Public documentation lanes">
|
||||
<div className="card-grid">
|
||||
{publicDocumentationPrinciples.map((principle) => (
|
||||
<article key={principle.title} className="card">
|
||||
<h3>{principle.title}</h3>
|
||||
<p>{principle.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
<PrincipleCardSection title="Public documentation lanes" cards={publicDocumentationPrinciples} />
|
||||
|
||||
<Section
|
||||
<BulletCardSection
|
||||
title="Current shipped capability"
|
||||
description="This section is the public manual's feature-registry-backed answer to the obvious question: what is actually live in HyperTwist today?"
|
||||
cards={featureAtlasCurrentTracks}
|
||||
/>
|
||||
|
||||
<StepCardSection
|
||||
title="Operator manual"
|
||||
description="This is the public-facing manual for how HyperTwist is actually used today: browser first for identity and release posture, desktop first for the simulator."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{operatorManualTracks.map((track) => (
|
||||
<article key={track.title} className="card">
|
||||
<h3>{track.title}</h3>
|
||||
<p>{track.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{track.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={operatorManualTracks}
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Current surface authority map"
|
||||
|
|
@ -611,117 +754,47 @@ export function DocsPage() {
|
|||
</Section>
|
||||
|
||||
<Section
|
||||
title="When to use browser versus desktop"
|
||||
description="The public manual is more useful when it says plainly which jobs stay on the web and which ones stay inside the native runtime."
|
||||
>
|
||||
<DeliverySurfaceResponsibilitiesGrid />
|
||||
</Section>
|
||||
|
||||
<BulletCardSection
|
||||
title="Simulator manual"
|
||||
description="These are the current product-safe usage tracks for the native runtime itself."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{simulatorManualCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={simulatorManualCards}
|
||||
/>
|
||||
|
||||
<Section
|
||||
<BulletCardSection
|
||||
title="Higher-dimensional family guide"
|
||||
description="These are the currently represented higher-dimensional lanes and the truthful host/runtime posture for each."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{higherDimensionalRuntimeGuideCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={higherDimensionalRuntimeGuideCards}
|
||||
/>
|
||||
|
||||
<Section
|
||||
<StepOnlyCardSection
|
||||
title="Deployment readiness manual"
|
||||
description="This is the public-safe checklist for moving from preview posture to operator-grade rollout."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{deploymentReadinessTracks.map((track) => (
|
||||
<article key={track.title} className="card">
|
||||
<h3>{track.title}</h3>
|
||||
<ul className="list top-gap">
|
||||
{track.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={deploymentReadinessTracks}
|
||||
/>
|
||||
|
||||
<Section
|
||||
<BulletCardSection
|
||||
title="Input and device posture"
|
||||
description="Public documentation should be explicit about the current control/runtime truth instead of blurring groundwork and finished VR claims together."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{inputAndDevicePostureCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={inputAndDevicePostureCards}
|
||||
/>
|
||||
|
||||
<Section
|
||||
<BulletCardSection
|
||||
title="Selectable control and settings roster"
|
||||
description="This manual section answers the concrete user question: what settings, profiles, selectors, and persistence surfaces are already real today?"
|
||||
>
|
||||
<div className="card-grid">
|
||||
{controlProfileRosterCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={controlProfileRosterCards}
|
||||
/>
|
||||
|
||||
<Section
|
||||
<BulletCardSection
|
||||
title="Runtime control guide"
|
||||
description="This manual section explains how to approach the current desktop runtime without pretending the currently closed XR/controller branch has already been reopened."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{runtimeControlGuideCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
cards={runtimeControlGuideCards}
|
||||
/>
|
||||
|
||||
<PublicPackagedDesktopProofSection
|
||||
platform={windowsValidationPlatform}
|
||||
|
|
@ -731,6 +804,12 @@ export function DocsPage() {
|
|||
]}
|
||||
/>
|
||||
|
||||
<FaqCardSection
|
||||
title="Common operator questions"
|
||||
description="These answers keep the public manual direct about the current browser shell, downloadable runtime, and bounded XR/settings truth."
|
||||
cards={supportFaqs}
|
||||
/>
|
||||
|
||||
{releaseManifest.public_docs_url ? (
|
||||
<Section title="External docs portal">
|
||||
<a className="button button--primary" href={releaseManifest.public_docs_url} target="_blank" rel="noreferrer">
|
||||
|
|
@ -814,6 +893,44 @@ export function SupportPage() {
|
|||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Digital delivery workflow"
|
||||
description="Support can move faster when the operator-facing browser shell and the installed desktop runtime are described as one continuous delivery lane."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{digitalDeliveryCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Privacy and compliance boundary"
|
||||
description="Support and rollout guidance should stay clear about what belongs to browser identity, what belongs to the desktop runtime, and what remains legal/compliance follow-through."
|
||||
>
|
||||
<div className="card-grid">
|
||||
{privacyBoundaryCards.map((card) => (
|
||||
<article key={card.title} className="card">
|
||||
<h3>{card.title}</h3>
|
||||
<p>{card.description}</p>
|
||||
<ul className="list top-gap">
|
||||
{card.bullets.map((bullet) => (
|
||||
<li key={bullet}>{bullet}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Escalation map"
|
||||
description="This keeps support conversations concrete by separating account, package, runtime, and rollout/compliance problems instead of flattening them together."
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"lane": "Windows Unreal packaged validation",
|
||||
"result": "passed",
|
||||
"generated_at": "2026-06-23T10:27:31.6439999Z",
|
||||
"generated_at": "2026-06-24T12:53:32.7234772Z",
|
||||
"configuration": "Development",
|
||||
"skip_build": true,
|
||||
"smoke_map_count": 2,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export interface ReleasePlatformValidationSummary {
|
|||
const WINDOWS_VALIDATION_SUMMARY_FALLBACK: ReleasePlatformValidationSummary = {
|
||||
lane: 'Windows Unreal packaged validation',
|
||||
result: 'passed',
|
||||
generated_at: '2026-06-23T10:27:31.6439999Z',
|
||||
generated_at: '2026-06-24T12:53:32.7234772Z',
|
||||
configuration: 'Development',
|
||||
skip_build: true,
|
||||
smoke_map_count: 2,
|
||||
|
|
|
|||
|
|
@ -715,6 +715,16 @@ export const resourceCollections = [
|
|||
] as const
|
||||
|
||||
export const changelogEntries = [
|
||||
{
|
||||
date: 'June 24, 2026',
|
||||
title: 'Auth and release fallback posture now reads as an operator workflow, not scattered warnings',
|
||||
details: 'Login, protected-route loading, dashboard auth health, and public/protected release-authority fallback surfaces now share a bounded recovery-guidance shape that preserves what still works, what stays intentionally withheld, and the next best move while live authority is degraded.',
|
||||
},
|
||||
{
|
||||
date: 'June 24, 2026',
|
||||
title: 'Higher-dimensional packaged proof refreshed again from the maintained Windows lane',
|
||||
details: 'The maintained `phase10validate` Windows validation root repackaged the higher-dimensional desktop lane into a fresh `phase6c_higherdim_refresh_20260624` archive, smoke-launched both dedicated-family maps from the packaged executable, and refreshed the public packaged-validation summary so browser release surfaces stay anchored to current native proof rather than stale yesterday-only evidence.',
|
||||
},
|
||||
{
|
||||
date: 'June 24, 2026',
|
||||
title: 'Protected operator routes now mirror packaged release proof',
|
||||
|
|
|
|||
|
|
@ -546,14 +546,15 @@ img {
|
|||
}
|
||||
|
||||
.auth-card {
|
||||
width: min(540px, 100%);
|
||||
width: min(760px, 100%);
|
||||
padding: 1.6rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
|
|
@ -584,11 +585,9 @@ img {
|
|||
.provider-row {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.auth-card__footer {
|
||||
margin-top: 1rem;
|
||||
color: var(--ht-muted);
|
||||
}
|
||||
|
||||
|
|
@ -694,6 +693,22 @@ code {
|
|||
color: var(--ht-muted);
|
||||
}
|
||||
|
||||
.general-page-loader-body {
|
||||
margin: 0;
|
||||
max-width: 32rem;
|
||||
text-align: center;
|
||||
color: var(--ht-muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.general-page-loader-extra {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.general-page-loader-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
color: var(--ht-cyan);
|
||||
text-shadow:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue