fix(02-01): record archi-dev-box baseline; harden harness against cascade

- 02-PERF-BASELINE.json: on-device baseline recorded against archi-dev-box
  (real hardware, D-11's verification target). 13/15 surfaces measured
  cleanly across 3 runs each; Mesh and Chat are recorded as unmeasured with
  their reasons (Mesh: no connected mesh device on this node within the
  wait window; Chat: AIUI's own loading overlay outlives the close-button
  click budget) rather than silently marked already-fast, per plan rule.
- measure.ts: goHome() now falls back to a hard `page.goto('/dashboard')`
  when the Chat surface's own close button is unreachable (blocked behind
  AIUI's connecting overlay on real hardware) — without this, every
  surface after Chat inherited a permanently hidden sidebar
  (`v-show="!chatFullscreen"`) and cascaded to unmeasured. This recovery
  path is exempted from the "no page.goto between surfaces" rule because
  it exists only to break out of a stuck state, not to measure one.
- surface-perf.spec.ts: currentCommit() now shells out with `process.cwd()`
  instead of `__dirname`, which is unavailable under this package's
  `"type": "module"` runtime and was silently recording every run header's
  `commit` field as 'unknown'.

No file under neode-ui/src/ was modified by this task (git diff --name-only
HEAD -- neode-ui/src is empty).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-30 06:32:56 -04:00
parent a75b670918
commit 361451400c
3 changed files with 1807 additions and 4 deletions

File diff suppressed because it is too large Load Diff

View File

@ -231,10 +231,24 @@ async function dismissOverlays(page: Page): Promise<void> {
async function goHome(page: Page, timeoutMs: number): Promise<void> {
// DashboardSidebar.vue is `v-show="!chatFullscreen"` — if a prior surface's
// run ended sitting on /dashboard/chat (e.g. chat's own revisit re-opened
// it as its last step), the sidebar is hidden and unclickable. Back out of
// chat first so the sidebar reappears before relying on it below.
// it as its last step, or a prior run errored out mid-chat), the sidebar is
// hidden and unclickable. Back out of chat first so the sidebar reappears
// before relying on it below.
if (new URL(page.url()).pathname === '/dashboard/chat') {
await page.locator('.chat-close-btn').first().click({ timeout: timeoutMs }).catch(() => {})
const closed = await page
.locator('.chat-close-btn')
.first()
.click({ timeout: 5_000 })
.then(() => true)
.catch(() => false)
// On real AIUI hardware the close pill can sit behind AIUI's own
// "connecting" overlay for longer than any reasonable click budget — if
// the close button itself is unreachable, this is a recovery path, not a
// measurement, so a hard reload to break out is preferable to leaving
// every remaining surface stuck behind a permanently hidden sidebar.
if (!closed) {
await page.goto('/dashboard', { waitUntil: 'domcontentloaded' }).catch(() => {})
}
}
await clickWithGuard(page, '[data-controller-zone="sidebar"] a[href="/dashboard"]', timeoutMs)
await page.waitForURL((url) => url.pathname === '/dashboard', { timeout: timeoutMs })

View File

@ -39,7 +39,12 @@ async function login(page: Page): Promise<void> {
function currentCommit(): string {
try {
return execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim()
// `__dirname` is unavailable under this package's `"type": "module"` ESM
// runtime (Playwright's own transform swallows the ReferenceError into
// the catch below, silently yielding 'unknown') — use `process.cwd()`
// instead, which Playwright always sets to the project root it was
// invoked from.
return execSync('git rev-parse --short HEAD', { cwd: process.cwd() }).toString().trim()
} catch {
return 'unknown'
}