Fix website auth overlay deploy path

This commit is contained in:
axiomlogicnexus 2026-07-02 22:05:28 +00:00
parent 11dd748dc5
commit 85b851184e
8 changed files with 100 additions and 9 deletions

View file

@ -57,3 +57,19 @@ steps:
- npm test
when:
- event: [push, pull_request, manual, tag]
website_live_deploy:
image: powershell
depends_on:
- website_frontend_validation
- website_auth_validation
commands:
- Set-Location repo/website
- $identityFile = if ($env:HYPERTWIST_VPS_SSH_KEY_PATH) { $env:HYPERTWIST_VPS_SSH_KEY_PATH } else { 'C:\ScriptoriumAI\ops\ssh\scriptorium_vps_ed25519' }
- $manifestPath = if ($env:HYPERTWIST_SAME_ORIGIN_MANIFEST) { $env:HYPERTWIST_SAME_ORIGIN_MANIFEST } else { 'deploy/hypertwist.same-origin.preview.bundle.example.json' }
- if (-not (Test-Path -LiteralPath $identityFile)) { throw "HyperTwist live deploy SSH key not found at '$identityFile'. Set HYPERTWIST_VPS_SSH_KEY_PATH on the Woodpecker builder." }
- if (-not (Test-Path -LiteralPath $manifestPath)) { throw "HyperTwist same-origin manifest not found at '$manifestPath'. Set HYPERTWIST_SAME_ORIGIN_MANIFEST or commit the intended manifest." }
- npm run run:vps-same-origin-live-deploy -- --manifest $manifestPath --identity-file $identityFile --wait-seconds 60
when:
- event: push
branch: main

View file

@ -33,11 +33,16 @@ Current truthful interpretation:
- the live public origin is still a preview deployment rather than full public
launch because the current auth/launch JSON still reports missing Windows
download, checkout, billing-map, and webhook-secret launch requirements
- a plain repo push is not itself a production deploy; live website changes are
only published after
- as of `2026-07-02`, `.woodpecker/hypertwist-bootstrap.yml` includes a
`website_live_deploy` step for `main` pushes that invokes
`npm run run:vps-same-origin-live-deploy -- --manifest <file> --identity-file <key>`
or an equivalent CI job that invokes the same helper and revalidates the
public origin
after website frontend and auth-server validation
- a repo push only counts as published after that helper-backed CI deploy, or a
manual invocation of the same helper, completes and revalidates the public
origin
- login/register overlay safety is now part of the browser launch invariant:
the decorative auth backdrop may render, but the auth form must stay visibly
above it on the live public origin
This is the next operator step after:

View file

@ -430,6 +430,11 @@ When a new packet materially adds or changes a normalized feature:
review path
- live commerce/download/auth configuration plus physical headset/controller
observation remain explicit launch gates
- the same browser-account lane now treats auth-shell backdrop stacking as a
launch-readiness invariant rather than decoration-only CSS: `/login` and
`/register` must render their forms above the fixed decorative/auth backdrop,
and the website test suite now carries both source-level and browser-level
regression proof for that condition
- this is a copy and layout coherence hardening pass, not a topology widening
and not a promotion of the optional full-browser simulator branch

View file

@ -46,9 +46,14 @@ Before using these templates:
to stage the committed checkout, upload the rendered bundle, install env,
build the site, replace the live `systemd` plus `nginx` files, and validate
the public origin through the same readiness lane
- a plain git push does **not** publish `https://hypertwist.app` by itself;
production changes are only live after this helper, or a CI job that invokes
the same helper, has completed and the public origin has been revalidated
- as of `2026-07-02`, `.woodpecker/hypertwist-bootstrap.yml` includes a
`website_live_deploy` step for `main` pushes that invokes this same helper
after website frontend and auth-server validation, using
`HYPERTWIST_VPS_SSH_KEY_PATH` and `HYPERTWIST_SAME_ORIGIN_MANIFEST` when the
builder provides them
- a git push only counts as published after that helper-backed CI deploy, or a
manual invocation of the same helper, has completed and the public origin has
been revalidated
- set `publicOrigin` to the exact live origin that should own both the website
and auth cookies; for the canonical production lane that value is
`https://hypertwist.app`

View file

@ -1,3 +1,5 @@
import fs from 'node:fs'
import path from 'node:path'
import { beforeEach, describe, expect, it } from 'vitest'
import {
AUTH_SHELL_BACKDROP_ID,
@ -6,6 +8,12 @@ import {
syncAuthShellBackdrop,
} from '../auth/auth-shell-backdrop'
function readAuthShellCssRule() {
const stylesPath = path.resolve(process.cwd(), 'src/styles/global.css')
const styles = fs.readFileSync(stylesPath, 'utf8')
return styles.match(/\.auth-shell\s*\{(?<rule>[^}]*)\}/m)?.groups?.rule || ''
}
describe('auth-shell-backdrop', () => {
beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>'
@ -31,6 +39,14 @@ describe('auth-shell-backdrop', () => {
expect(backdrop.style.zIndex).toBe('0')
})
it('keeps auth route content above the decorative backdrop layer', () => {
const authShellRule = readAuthShellCssRule()
expect(authShellRule).toContain('position: relative')
expect(authShellRule).toContain('z-index: 1')
expect(authShellRule).toContain('isolation: isolate')
})
it('removes the auth backdrop when navigation leaves auth routes', () => {
syncAuthShellBackdrop('/login')
expect(document.getElementById(AUTH_SHELL_BACKDROP_ID)).not.toBeNull()

View file

@ -704,6 +704,7 @@ export const deploymentReadinessTracks = [
title: '1. Identity and access posture',
steps: [
'Confirm shared browser auth is live before inviting operators into the protected release lane.',
'Treat login/register visibility above decorative auth backdrops as a launch-readiness invariant, not a cosmetic preference.',
'Use the protected dashboard to resolve plan, entitlement, and desktop-link token posture.',
'Keep browser-to-desktop handoff explicit so the installed app never depends on password reuse.',
],
@ -1494,6 +1495,11 @@ export const releasePacketCards = [
] as const
export const changelogEntries = [
{
date: 'July 2, 2026',
title: 'Browser login shell now has explicit overlay-safety proof before live deploy',
details: 'Login and registration keep the decorative auth backdrop, but the auth shell now owns a higher stacking layer so production users do not see an opaque overlay over the form. The responsive public-route suite now verifies the auth shell sits above the same-origin auth backdrop, and the main-branch CI path now follows validation with the same-origin live-deploy helper so pushed website fixes can reach hypertwist.app through the supported production chain.',
},
{
date: 'June 30, 2026',
title: 'Downloadable operator manual now mirrors the richer public manual routes',

View file

@ -719,6 +719,9 @@ img {
.auth-shell {
min-height: 100vh;
position: relative;
z-index: 1;
isolation: isolate;
display: grid;
place-items: center;
padding: 1.25rem;

View file

@ -11,7 +11,7 @@ type PublicRouteExpectation = {
const responsivePublicRoutes: readonly PublicRouteExpectation[] = [
{
path: '/',
heading: 'HyperTwist turns cube practice into a real operator-grade training stack.',
heading: 'From first solves to 120-cell, HyperTwist keeps the real simulator in the downloadable.',
cta: 'Download desktop app',
},
{
@ -57,7 +57,7 @@ const responsivePublicRoutes: readonly PublicRouteExpectation[] = [
},
{
path: '/changelog',
heading: 'Recent public-facing HyperTwist changes.',
heading: 'Release notes you can actually use for rollout.',
},
{
path: '/open-source-notices',
@ -95,9 +95,44 @@ async function assertResponsivePublicRoute(page: Page, route: PublicRouteExpecta
}
}
if (route.path === '/login' || route.path === '/register') {
await expectAuthShellAboveBackdrop(page)
}
await expectNoHorizontalOverflow(page)
}
async function expectAuthShellAboveBackdrop(page: Page) {
await expect(page.locator('#hypertwist-auth-shell-backdrop')).toHaveCount(1)
const stacking = await page.evaluate(() => {
const backdrop = document.getElementById('hypertwist-auth-shell-backdrop')
const shell = document.querySelector('.auth-shell')
if (!(backdrop instanceof HTMLElement) || !(shell instanceof HTMLElement)) {
return null
}
const backdropStyle = window.getComputedStyle(backdrop)
const shellStyle = window.getComputedStyle(shell)
return {
backdropPointerEvents: backdropStyle.pointerEvents,
backdropZIndex: backdropStyle.zIndex,
shellIsolation: shellStyle.isolation,
shellPosition: shellStyle.position,
shellZIndex: shellStyle.zIndex,
}
})
expect(stacking).toEqual({
backdropPointerEvents: 'none',
backdropZIndex: '0',
shellIsolation: 'isolate',
shellPosition: 'relative',
shellZIndex: '1',
})
}
test.describe('responsive public routes (mobile)', () => {
test.use({ viewport: { width: 390, height: 844 } })