Merge pull request #1510 from BradGroux/fix/native-runner-height-1387

fix: make native matrix runner-height safe
This commit is contained in:
Brad Groux 2026-09-04 15:33:16 -05:00 • committed by GitHub
commit fb7dc0f4e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 31 additions and 10 deletions

View file

@ -6,7 +6,7 @@ Status: in development. This gate is not yet release acceptance and #1387 remain
`pnpm desktop:ui:mac <candidate.app> <new-evidence-directory>` launches that actual packaged Electron executable through Playwright. It verifies the production preload identity, embedded build SHA, package path, and disposable user-data path. It does not start Vite, inject a fake desktop bridge, replace the installed app, or use the operator's task data. Authentication and task/template persistence use the isolated packaged server. The text-artifact preview is explicitly identified as read-only fixture data; it does not certify PDF rendering, provider execution, or artifact registration.
Build the candidate with `VERITAS_BUILD_SHA` set to the exact candidate commit before running. The fixed matrix covers normal 1700×1000 and minimum 1180×760 content sizes in both themes at 100% zoom. These are native content dimensions, not a claim that the outer window has those dimensions. Evidence separately records outer bounds, content bounds, display scale, minimum window size, macOS version, and app version/build.
Build the candidate with `VERITAS_BUILD_SHA` set to the exact candidate commit before running. The fixed matrix covers expanded-width 1700×760 and minimum 1180×760 content sizes in both themes at 100% zoom. The shared 760px height is deliberate: hosted macOS runners expose a 760px usable content area, while the two widths still exercise expanded and compact shell behavior. These are native content dimensions, not a claim that the outer window has those dimensions. Evidence separately records outer bounds, content bounds, display scale, minimum window size, macOS version, and app version/build.
The report hashes the entire bundle, including server/web resources and framework binaries. Each state retains its route, geometry, native `BrowserWindow.capturePage()` PNG, and screenshot digest. Captures wait for active overlays and their ancestors to become opaque, then allow two animation frames for painting. These are native renderer captures, not pictures of the macOS menu bar or window shadow. A failed state remains in the required matrix. Startup failures are recorded without taking authentication screenshots or disclosing passwords/recovery keys. Temporary test profiles are retained outside the repository and contain synthetic data only.
@ -24,6 +24,8 @@ The first complete development matrix reproduced Session menu Escape dismissal f
The diagnostic run completed at `2026-09-04T06:38:33.197Z` with 144 recorded states and four interaction failures, all Session menu Escape checks. The stricter verifier additionally rejected the normal light Workflows capture because the native content size changed to 1710×1073 instead of 1700×1000. The cause of that window-size change is not established. Capture/completion mode assertions now also fail the scenario immediately when this happens; the runner's earlier 140-pass count is not an accepted matrix. Final native verification must rerun the latest committed runner.
The first signed 6.1.7 capture run then proved the 1700×1000 target itself was not portable: the hosted macOS window manager returned 1700×760 for every normal resize, so that single failed precondition cascaded across the normal matrix and all seeded probes. The current contract centralizes runner-safe content sizes for native verification and documentation capture. This is a harness correction, not accepted evidence; a new commit-bound signed candidate must pass the complete matrix.
These diagnostic runs use an unsigned 6.1.6 package built from `afb447156fd77d24ee4616bdd7f8e556371c4925`, with whole-bundle SHA-256 `52dad82a58e5352c77577d9547323f1ff1cece318e94a22ea415bd93104bc183`. The harness checkout was dirty. They are not clean-commit release acceptance, installed-app verification, or refreshed documentation media. Committing this harness changes HEAD and requires a newly built candidate for final acceptance; old evidence must not be relabeled.
## Remaining acceptance work

View file

@ -4,4 +4,4 @@ Escape closes the Session menu when focus is on its trigger or inside the accoun
The previous Mantine dropdown handled Escape during capture inside the portaled content only. The trigger was outside that path. The fix handles bubbling key events on both the trigger and dropdown, disables the dropdown's capture handler, and restores focus only for an unconsumed Escape.
The focused layout-chrome regressions reproduce the original failure in both trigger presentations and cover content focus, nested event ownership, mouse toggle, click-away, and identity/security callbacks. Packaged macOS acceptance uses the candidate-bound runner from #1387 at 1700×1000 and 1180×760 in both themes. A passing candidate does not establish signing, installation, refreshed documentation media, or release publication.
The focused layout-chrome regressions reproduce the original failure in both trigger presentations and cover content focus, nested event ownership, mouse toggle, click-away, and identity/security callbacks. Packaged macOS acceptance uses the candidate-bound runner from #1387 at 1700×760 and 1180×760 in both themes. A passing candidate does not establish signing, installation, refreshed documentation media, or release publication.

View file

@ -7,7 +7,7 @@ import path from 'node:path';
import os from 'node:os';
import { chromium, expect } from '@playwright/test';
import { createNativeSession } from '../native-ui/session.mjs';
import { fileDigest, packageDigest } from '../native-ui/contract.mjs';
import { contentSizes, fileDigest, packageDigest } from '../native-ui/contract.mjs';
import { maintainedAssets, mediaSchema, mediaEvidenceFailures } from './verify.mjs';
import { encodeInteraction, recordInteraction } from './record.mjs';
import { finalizeCapture } from './finalize.mjs';
@ -245,11 +245,14 @@ try {
const launched = await session.launch();
({ app, page } = launched);
report.identity = launched.identity;
await app.evaluate(({ BrowserWindow }) => {
await app.evaluate(({ BrowserWindow }, sizes) => {
const win = BrowserWindow.getAllWindows().find((w) => w.isVisible());
win.webContents.setZoomFactor(1);
win.setContentSize(1700, 1000);
});
win.setContentSize(sizes.normal.width, sizes.normal.height);
}, contentSizes);
await expect
.poll(() => page.evaluate(() => [innerWidth, innerHeight]))
.toEqual([contentSizes.normal.width, contentSizes.normal.height]);
await theme();
// Use the actual isolated packaged API and its authenticated cookie, no auth bypass.
for (const [name, status, type] of [

View file

@ -35,9 +35,15 @@ export const settingsSections = [
'Security',
'Maintenance',
];
export const contentSizes = {
// Hosted macOS runners expose only 760px of usable content height. Keep the
// expanded width while using a height the native window manager can honor.
normal: { width: 1700, height: 760 },
minimum: { width: 1180, height: 760 },
};
export const modes = ['light', 'dark'].flatMap((theme) => [
{ id: `${theme}-normal`, theme, width: 1700, height: 1000 },
{ id: `${theme}-minimum`, theme, width: 1180, height: 760 },
{ id: `${theme}-normal`, theme, ...contentSizes.normal },
{ id: `${theme}-minimum`, theme, ...contentSizes.minimum },
]);
// The runner cannot reduce this list when a scenario fails or is not implemented.
export const states = [

View file

@ -2,6 +2,7 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import {
contentSizes,
evidenceFailures,
geometryFailures,
modes,
@ -22,6 +23,14 @@ import path from 'node:path';
const commit = 'a'.repeat(40);
const digest = 'b'.repeat(64);
const now = Date.now();
test('native content sizes fit the hosted macOS runner while retaining expanded width', () => {
assert.deepEqual(contentSizes, {
normal: { width: 1700, height: 760 },
minimum: { width: 1180, height: 760 },
});
assert.equal(modes.length, 4);
assert(modes.every((mode) => mode.height === 760));
});
test('bundle identity includes web resources and rejects external symlinks', async (t) => {
const fixture = await mkdtemp(path.join(os.tmpdir(), 'native-ui-contract-'));
t.after(() => rm(fixture, { recursive: true, force: true }));

View file

@ -10,6 +10,7 @@ import {
fileDigest,
evidenceFailures,
geometryFailures,
contentSizes,
modes,
packageDigest,
routes,
@ -513,13 +514,13 @@ async function exercise(state, mode, shot) {
await shot();
await dismiss(dialog);
} else if (state === 'responsive-collapse') {
await resize(1700, 1000);
await resize(contentSizes.normal.width, contentSizes.normal.height);
if (await button('Expand left sidebar').isVisible())
await button('Expand left sidebar').click();
if (await button('Expand right sidebar').isVisible())
await button('Expand right sidebar').click();
await button('Open Board Chat').click();
await resize(1180, 760);
await resize(contentSizes.minimum.width, contentSizes.minimum.height);
await expect(page.getByRole('region', { name: 'Workbench right dock' })).toBeHidden();
await expect(button('Expand left sidebar')).toBeVisible();
await expect(button('Expand right sidebar')).toBeVisible();