Files
hanzo.ai/e2e/gates/rooms.spec.ts
zeekayandHanzo Dev 9bbdc64ee0 One /dev: the builder is platform.hanzo.ai/dev
This export carried a second copy of the builder at /dev — the composer,
a run's transcript and a project pane (app/(app)/dev and
components/workspace/{Build,Session,project}.tsx), plus DevStudio.tsx and
enter.ts, which nothing else used. They are deleted, and dev.hanzo.ai is no
longer published from this export.

Every link to the builder is DEV or dev(ref) from lib/host.ts. dev() carries
a run id or a project slug into the path only when it is exactly one, so no
caller can make it name another address. The shell's Dev tile, Projects and
Runs open it there; /code forwards there. The edge's permanent redirects for
hanzo.ai/dev, /code, dev.hanzo.ai and hanzo.app live in universe.

Gates and specs that drove the old /dev drive /chat where they tested the
shared shell, and are removed where the builder itself was the subject.



Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 15:53:59 -07:00

65 lines
2.2 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { serveExport } from './export'
import { signIn } from '../session'
/**
* EVERY ROOM IN THE SHIPPING BYTES DRAWS ITS OWN AFFORDANCE.
*
* The gates above prove the export has pages and that they are indexable; this
* proves the ROOMS still work, which is a different failure: a blank pane and a
* broken one both ship, both index, and both hold their shape at every width.
*
* Subject is `out/` and there is no backend behind it, which is the point — a
* room that only works when the platform answers is a room that shows a reader
* nothing on the day it does not. Each expectation is the thing that room is
* FOR, and every room must reach it with nothing answering at all.
*/
const ROOMS: [string, RegExp][] = [
['/work', /working|no runs yet|start a run/i],
['/inbox', /inbox|nothing has arrived/i],
['/drive', /drive/i],
['/board', /board/i],
]
test.describe('the app surface ships working rooms', () => {
let base: string
let stop: () => Promise<void>
test.beforeAll(async () => {
const served = await serveExport()
base = served.url
stop = served.close
})
test.afterAll(async () => stop())
for (const [route, wants] of ROOMS) {
test(`${route} draws its own affordance`, async ({ page }) => {
await signIn(page)
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto(`${base}${route}`)
const room = page.locator('main')
await expect(room).toBeVisible({ timeout: 20_000 })
await expect
.poll(async () => (await room.innerText()).replace(/\s+/g, ' '), { timeout: 20_000 })
.toMatch(wants)
// And nothing in it is the estate talking to itself.
const text = await room.innerText()
// 'Failed to fetch' is the BROWSER's phrasing for a store that did not
// answer, and it reached readers because this list was written from the
// mounts' words rather than from everything a reader can end up seeing.
for (const leak of [
'no instance running',
'ECONNREFUSED',
'Load failed',
'Failed to fetch',
'TypeError',
]) {
expect(text, `"${leak}" is plumbing, not an answer`).not.toContain(leak)
}
})
}
})