mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-06 12:51:54 +00:00
Checkpoint portable TORCH fleet, provider adapters and owner dashboard
This commit is contained in:
1 parent
d31437a4fb
commit
03fcbdd23f
140 files changed
+26152
-757
No files matched your search
@@ -0,0 +1,15 @@
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
// Condition and measurement subprocesses both contact the same browser service.
|
||||
const [base, operation] = process.argv.slice(2);
|
||||
assert.ok(['probe', 'measure'].includes(operation));
|
||||
const response = await fetch(`${base}/${operation}`, {
|
||||
method: operation === 'measure' ? 'POST' : 'GET', signal: AbortSignal.timeout(10_000),
|
||||
});
|
||||
assert.equal(response.status, 200);
|
||||
const report = await response.json();
|
||||
if (operation === 'probe') {
|
||||
assert.equal(report.subject.commit, process.env.TORCH_CHECK_COMMIT);
|
||||
assert.equal(report.subject.inputDigest, process.env.TORCH_CHECK_INPUT_DIGEST);
|
||||
}
|
||||
console.log(JSON.stringify(report));
|
||||
@@ -0,0 +1,86 @@
|
||||
import { once } from 'node:events';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createServer } from 'node:http';
|
||||
import { join } from 'node:path';
|
||||
import { chromium } from '@playwright/test';
|
||||
|
||||
// Explicit qualification service: one page survives all probe/measurement
|
||||
// commands. It is not a provider, launcher, or installed TORCH service.
|
||||
let subject;
|
||||
let page;
|
||||
let browser;
|
||||
let measurements = 0;
|
||||
let mode;
|
||||
const server = createServer(async (request, response) => {
|
||||
try {
|
||||
if (request.url === '/page' && subject) {
|
||||
response.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-store' });
|
||||
response.end(readFileSync(join(subject.root, 'dist', 'index.html')));
|
||||
return;
|
||||
}
|
||||
if (!page) { response.writeHead(503).end(); return; }
|
||||
let result;
|
||||
if (request.url === '/probe' && request.method === 'GET') {
|
||||
const observed = await page.evaluate(async () => {
|
||||
const before = window.simulationClock;
|
||||
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
return {
|
||||
commit: document.querySelector('[data-commit]').dataset.commit,
|
||||
runtimeId: window.runtimeId,
|
||||
conditions: { 'clock-moving': window.simulationClock > before,
|
||||
visible: document.visibilityState === 'visible',
|
||||
build: document.querySelector('[data-build]').dataset.build },
|
||||
};
|
||||
});
|
||||
result = { schema: 'torch.dev/check-conditions/v1alpha1',
|
||||
subject: { commit: observed.commit, inputDigest: subject.inputDigest },
|
||||
runtimeId: observed.runtimeId, conditions: observed.conditions };
|
||||
} else if (request.url === '/measure' && request.method === 'POST') {
|
||||
measurements += 1;
|
||||
result = await page.evaluate((freeze) => {
|
||||
if (freeze) window.clockFrozen = true;
|
||||
return { measurement: 'green', runtimeId: window.runtimeId };
|
||||
}, mode === 'drift');
|
||||
if (mode === 'replaced') await page.reload();
|
||||
} else if (request.url === '/status' && request.method === 'GET') {
|
||||
result = { measurements, runtimeId: await page.evaluate(() => window.runtimeId) };
|
||||
} else { response.writeHead(404).end(); return; }
|
||||
response.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
|
||||
response.end(JSON.stringify(result));
|
||||
} catch (error) {
|
||||
response.writeHead(500).end(JSON.stringify({ error: error.message }));
|
||||
}
|
||||
});
|
||||
|
||||
async function close() {
|
||||
await browser?.close();
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
}
|
||||
|
||||
process.on('message', async (message) => {
|
||||
try {
|
||||
if (message.action === 'load') {
|
||||
subject = message.subject;
|
||||
mode = message.mode;
|
||||
browser = await chromium.launch({ headless: true });
|
||||
page = await browser.newPage();
|
||||
const base = `http://127.0.0.1:${server.address().port}`;
|
||||
await page.route('**/*', (route) => route.request().url().startsWith(`${base}/`)
|
||||
? route.continue() : route.abort());
|
||||
await page.goto(`${base}/page`);
|
||||
if (mode === 'frozen') await page.evaluate(() => { window.clockFrozen = true; });
|
||||
process.send({ event: 'loaded', runtimeId: await page.evaluate(() => window.runtimeId) });
|
||||
} else if (message.action === 'close') {
|
||||
await close();
|
||||
process.disconnect();
|
||||
}
|
||||
} catch (error) {
|
||||
process.send({ event: 'error', message: error.message });
|
||||
await close();
|
||||
process.exitCode = 1;
|
||||
process.disconnect();
|
||||
}
|
||||
});
|
||||
server.listen(0, '127.0.0.1');
|
||||
await once(server, 'listening');
|
||||
process.send({ event: 'listening', base: `http://127.0.0.1:${server.address().port}` });
|
||||
@@ -0,0 +1,53 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { once } from 'node:events';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createServer } from 'node:http';
|
||||
import { join } from 'node:path';
|
||||
import { chromium } from '@playwright/test';
|
||||
|
||||
// Qualification harness, not a bundled product dependency. Run from the captured
|
||||
// input root; module dependencies resolve from this explicitly configured harness.
|
||||
const expectedBuild = process.argv[2];
|
||||
assert.ok(expectedBuild, 'Expected build must be declared by the scenario');
|
||||
const root = process.cwd();
|
||||
const server = createServer((request, response) => {
|
||||
if (request.url !== '/') {
|
||||
response.writeHead(404).end();
|
||||
return;
|
||||
}
|
||||
response.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-store' });
|
||||
response.end(readFileSync(join(root, 'dist', 'index.html')));
|
||||
});
|
||||
let browser;
|
||||
try {
|
||||
server.listen(0, '127.0.0.1');
|
||||
await once(server, 'listening');
|
||||
const base = `http://127.0.0.1:${server.address().port}`;
|
||||
browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
await page.route('**/*', (route) => route.request().url().startsWith(`${base}/`)
|
||||
? route.continue() : route.abort());
|
||||
await page.goto(base);
|
||||
const observation = await page.evaluate(async () => {
|
||||
const before = performance.now();
|
||||
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
return {
|
||||
build: document.querySelector('[data-build]').dataset.build,
|
||||
commit: document.querySelector('[data-commit]').dataset.commit,
|
||||
visible: document.visibilityState === 'visible',
|
||||
browserClockMoving: performance.now() > before,
|
||||
};
|
||||
});
|
||||
assert.deepEqual(errors, []);
|
||||
assert.equal(observation.build, expectedBuild);
|
||||
assert.equal(observation.commit, process.env.TORCH_CHECK_COMMIT);
|
||||
assert.equal(observation.visible, true);
|
||||
assert.equal(observation.browserClockMoving, true);
|
||||
assert.match(process.env.TORCH_CHECK_INPUT_DIGEST, /^[a-f0-9]{64}$/);
|
||||
console.log(JSON.stringify({ ...observation, inputDigest: process.env.TORCH_CHECK_INPUT_DIGEST }));
|
||||
} finally {
|
||||
await browser?.close();
|
||||
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { once } from 'node:events';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createServer } from 'node:http';
|
||||
import { join } from 'node:path';
|
||||
import { chromium } from '@playwright/test';
|
||||
|
||||
const token = randomUUID();
|
||||
let browserServer;
|
||||
let page;
|
||||
let finish;
|
||||
const stopped = new Promise((resolve) => { finish = resolve; });
|
||||
const server = createServer(async (request, response) => {
|
||||
try {
|
||||
if (request.url === '/' && request.method === 'GET') {
|
||||
response.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
response.end(readFileSync(join(process.cwd(), 'dist', 'index.html')));
|
||||
} else if (request.url === '/status' && page) {
|
||||
const observed = await page.evaluate(() => ({ build: document.querySelector('[data-build]').dataset.build,
|
||||
commit: document.querySelector('[data-commit]').dataset.commit, visible: document.visibilityState === 'visible' }));
|
||||
response.writeHead(200, { 'Content-Type': 'application/json' }).end(JSON.stringify(observed));
|
||||
} else if (request.url === '/stop' && request.method === 'POST' && request.headers['x-fixture-token'] === token) {
|
||||
const processHandle = browserServer.process();
|
||||
const exited = once(processHandle, 'exit');
|
||||
await browserServer.close();
|
||||
await exited;
|
||||
response.writeHead(200, { 'Content-Type': 'application/json' }).end(JSON.stringify({ stopped: true }));
|
||||
server.close(finish);
|
||||
} else response.writeHead(404).end();
|
||||
} catch (error) { response.writeHead(500).end(JSON.stringify({ error: error.message })); }
|
||||
});
|
||||
try {
|
||||
server.listen(0, '127.0.0.1');
|
||||
await once(server, 'listening');
|
||||
const base = `http://127.0.0.1:${server.address().port}`;
|
||||
browserServer = await chromium.launchServer({ headless: true });
|
||||
const browser = await chromium.connect(browserServer.wsEndpoint());
|
||||
page = await browser.newPage();
|
||||
await page.route('**/*', (route) => route.request().url().startsWith(`${base}/`) ? route.continue() : route.abort());
|
||||
await page.goto(base);
|
||||
const commit = await page.locator('[data-commit]').getAttribute('data-commit');
|
||||
assert.equal(commit, process.env.TORCH_CHECK_COMMIT);
|
||||
const browserPid = browserServer.process().pid;
|
||||
const stat = readFileSync(`/proc/${browserPid}/stat`, 'utf8');
|
||||
const browserStart = stat.slice(stat.lastIndexOf(')') + 2).trim().split(/\s+/)[19];
|
||||
const executorStat = readFileSync(`/proc/${process.pid}/stat`, 'utf8');
|
||||
const executorStart = executorStat.slice(executorStat.lastIndexOf(')') + 2).trim().split(/\s+/)[19];
|
||||
console.log(JSON.stringify({ event: 'browser-ready', executorPid: process.pid, executorStart, browserPid, browserStart, base, token }));
|
||||
await stopped;
|
||||
} finally {
|
||||
await browserServer?.close();
|
||||
if (server.listening) await new Promise((resolve) => server.close(resolve));
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { openControlPlane } from '../src/control-plane/service.mjs';
|
||||
import { CheckService } from '../src/checks/service.mjs';
|
||||
import { ResourceService } from '../src/resources/service.mjs';
|
||||
|
||||
const [repositoryRoot, preparedId, areaId] = process.argv.slice(2);
|
||||
const control = openControlPlane({ repositoryRoot });
|
||||
try {
|
||||
const resources = new ResourceService({ repositoryRoot, controlPlane: control });
|
||||
const checks = new CheckService({ repositoryRoot, controlPlane: control, resourceService: resources,
|
||||
executor: (command, args, options) => spawnSync(command, args, {
|
||||
...options, stdio: ['ignore', 'inherit', 'pipe'],
|
||||
}) });
|
||||
checks.runPrepared({ preparedId, areaId });
|
||||
} finally { control.close(); }
|
||||
@@ -0,0 +1,247 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { once } from 'node:events';
|
||||
import { chromium } from '@playwright/test';
|
||||
import { createConsoleServer } from '../src/console/server.mjs';
|
||||
|
||||
const server = createConsoleServer();
|
||||
server.listen(0, '127.0.0.1');
|
||||
await once(server, 'listening');
|
||||
const base = 'http://127.0.0.1:' + server.address().port;
|
||||
let browser;
|
||||
try {
|
||||
browser = await chromium.launch({ headless: true });
|
||||
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 }, reducedMotion: 'reduce' });
|
||||
const apiRequests = [];
|
||||
const errors = [];
|
||||
await context.route('**/api/**', (route) => {
|
||||
apiRequests.push(route.request().url());
|
||||
return route.abort();
|
||||
});
|
||||
context.on('page', (page) => page.on('pageerror', (error) => errors.push(error.message)));
|
||||
const landing = await context.newPage();
|
||||
await landing.goto(base);
|
||||
const popup = context.waitForEvent('page');
|
||||
await landing.getByRole('link', { name: 'View dashboard demo', exact: true }).click();
|
||||
const page = await popup;
|
||||
await page.waitForLoadState();
|
||||
await page.locator('#project-name').filter({ hasText: 'Northstar' }).waitFor();
|
||||
assert.equal(await page.title(), 'TORCH Dashboard');
|
||||
assert.equal(await page.locator('.agent-row').count(), 6);
|
||||
assert.equal(await page.locator('.task-ticket').count(), 5);
|
||||
assert.equal(await page.getByRole('region', { name: 'Queue', exact: true }).locator('.task-ticket').count(), 2);
|
||||
assert.equal(await page.locator('#demo-banner').isVisible(), true);
|
||||
assert.match(await page.locator('#operation-outcomes').innerText(), /Outcome unknown — review required/);
|
||||
assert.equal(await page.locator('#operation-outcomes button').count(), 0);
|
||||
await page.locator('#operation-outcomes .check-evidence').last().locator('summary').click();
|
||||
assert.match(await page.locator('#operation-outcomes').innerText(), /transient failure.*effects unknown/s);
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await page.locator('#operation-outcomes').screenshot({ path: 'reports/design-system/torch-dashboard-operation-outcomes.png' });
|
||||
await page.locator('#activity-review > summary').click();
|
||||
assert.match(await page.locator('#activity-review').innerText(), /4 days without a named commit/);
|
||||
assert.match(await page.locator('#activity-review').innerText(), /waiting may be expected/);
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await page.locator('#activity-review').screenshot({ path: 'reports/design-system/torch-dashboard-activity-review.png' });
|
||||
assert.match(await page.locator('#check-list').innerText(), /Running — completion unconfirmed/);
|
||||
await page.locator('#check-list .check-evidence').filter({ hasText: 'interrupted-browser' }).locator('summary').click();
|
||||
assert.match(await page.locator('#check-list').innerText(), /Abandoned — not qualified/);
|
||||
assert.match(await page.locator('#check-list').innerText(), /Captured-input cleanup pending/);
|
||||
assert.match(await page.locator('#check-list').innerText(), /owner-attested, not independently verified/);
|
||||
assert.equal(await page.locator('#check-list button').count(), 0);
|
||||
await page.locator('#check-list .check-evidence').filter({ hasText: 'recovery-scenario' }).locator('summary').click();
|
||||
assert.match(await page.locator('#check-list').innerText(), /clockMoving.*true/s);
|
||||
assert.match(await page.locator('#check-list').innerText(), /not independent sensor truth/);
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await page.locator('#check-list').screenshot({ path: 'reports/design-system/torch-dashboard-check-evidence.png' });
|
||||
assert.match(await page.locator('#owner-briefing').innerText(), /Choose the project home layout/);
|
||||
assert.match(await page.locator('#owner-briefing').innerText(), /not independent live verification/);
|
||||
await page.locator('#owner-briefing .briefing-detail').first().locator('summary').click();
|
||||
assert.match(await page.locator('#owner-briefing').innerText(), /Landed — not necessarily deployed/);
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await page.locator('#owner-briefing').screenshot({ path: 'reports/design-system/torch-dashboard-owner-briefing-desktop.png' });
|
||||
assert.match(await page.locator('#attention-list').innerText(), /manager launch needs inspection/);
|
||||
await page.locator('#attention-list a[href="#manager-wakes"]').click();
|
||||
assert.equal(await page.locator('#manager-wakes').isVisible(), true);
|
||||
assert.match(await page.locator('#manager-wake-list').innerText(), /Launch outcome unknown/);
|
||||
assert.match(await page.locator('#manager-wake-list').innerText(), /sample-wake-01/);
|
||||
assert.equal(await page.locator('#manager-wake-list button').count(), 0,
|
||||
'uncertain launches must not offer automatic restart or unlocking');
|
||||
const pilotPanel = page.locator('.organization-proposal').filter({ hasText: 'Coordination pilot' }).locator('.pilot-review-history');
|
||||
await pilotPanel.locator(':scope > summary').click();
|
||||
assert.match(await pilotPanel.innerText(), /Recommendation: inconclusive/);
|
||||
assert.match(await pilotPanel.innerText(), /reviewer-reported, not independently verified/);
|
||||
const comparison = pilotPanel.getByRole('table', { name: 'Baseline compared with pilot observations' });
|
||||
assert.equal(await comparison.locator('tbody td').nth(0).innerText(), '3');
|
||||
assert.equal(await comparison.locator('tbody td').nth(1).innerText(), 'About 2');
|
||||
assert.equal(await comparison.locator('tbody td').nth(2).innerText(), 'qualitative');
|
||||
await pilotPanel.getByText('Criterion outcomes and evidence references', { exact: true }).click();
|
||||
assert.match(await pilotPanel.innerText(), /Reduce cross-domain waiting.*unknown/);
|
||||
assert.match(await pilotPanel.innerText(), /Sample peer handoffs/);
|
||||
assert.equal(await pilotPanel.locator('button').count(), 0, 'review recommendations cannot change the organization');
|
||||
const conclusion = page.locator('[data-organization-review][data-type="hierarchy-conclusion"]');
|
||||
await conclusion.locator('..').locator(':scope > summary').click();
|
||||
await conclusion.locator('[name="action"]').selectOption('adopt');
|
||||
await conclusion.locator('[name="reason"]').fill('Keep the coordination role if the pilot supports it.');
|
||||
await conclusion.locator('[data-review-preview]').click();
|
||||
await conclusion.locator('[data-review-preview-panel]').waitFor();
|
||||
assert.equal(await conclusion.locator('[data-review-confirm]').isVisible(), false,
|
||||
'qualitative inconclusive evidence does not permit adoption');
|
||||
await conclusion.locator('[data-review-edit]').click();
|
||||
await conclusion.locator('[name="action"]').selectOption('reverse');
|
||||
await conclusion.locator('[name="reason"]').fill('Restore direct reporting while collecting better evidence.');
|
||||
await conclusion.locator('[data-review-preview]').click();
|
||||
await conclusion.locator('[data-review-confirm]').waitFor();
|
||||
await conclusion.getByText('Exact proposal and evidence in this decision', { exact: true }).click();
|
||||
assert.match(await conclusion.locator('[data-review-scope]').innerText(), /schedulesRetained/);
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await conclusion.screenshot({ path: 'reports/design-system/torch-dashboard-pilot-conclusion.png' });
|
||||
await conclusion.locator('[data-review-confirm]').click();
|
||||
await page.locator('#organization-action-status').filter({ hasText: 'recorded as reversed' }).waitFor();
|
||||
assert.equal(await page.locator('.agent-row').count(), 6);
|
||||
|
||||
const ticket = page.locator('.task-ticket').filter({ hasText: 'API-08' });
|
||||
await ticket.locator('.priority-change > summary').click();
|
||||
await ticket.locator('[name="priority"]').selectOption('urgent');
|
||||
await ticket.locator('[name="reason"]').fill('Needed for the beta');
|
||||
await ticket.getByRole('button', { name: 'Review change', exact: true }).click();
|
||||
await ticket.locator('[data-priority-confirm]').waitFor();
|
||||
assert.equal(await ticket.locator('.priority').textContent(), 'high');
|
||||
await ticket.locator('[data-priority-confirm]').click();
|
||||
await ticket.locator('.priority-urgent').waitFor();
|
||||
|
||||
await page.locator('.owner-request-composer > summary').click();
|
||||
await page.locator('#owner-request-recipient').selectOption('qa');
|
||||
await page.locator('#owner-request-body').fill('Please review the home layout.');
|
||||
await page.locator('[data-owner-request-preview]').click();
|
||||
await page.locator('[data-owner-request-confirm]').waitFor();
|
||||
await page.locator('[data-owner-request-confirm]').click();
|
||||
await page.locator('#message-list .conversation-item').filter({ hasText: 'Please review the home layout.' }).waitFor();
|
||||
|
||||
const feedback = page.locator('.artifact-feedback-form').first();
|
||||
await feedback.locator('textarea').fill('Give active work more room.');
|
||||
await feedback.getByRole('button', { name: 'Review feedback' }).click();
|
||||
await feedback.locator('[data-feedback-confirm]').waitFor();
|
||||
await feedback.locator('[data-feedback-confirm]').click();
|
||||
await page.locator('.artifact-feedback blockquote').filter({ hasText: 'Give active work more room.' }).waitFor();
|
||||
|
||||
await page.locator('.runtime-profile-tools > summary').click();
|
||||
await page.locator('#runtime-profile-area').selectOption('interface');
|
||||
await page.locator('#runtime-profile-runtime').selectOption('claude');
|
||||
assert.equal(await page.locator('#runtime-profile-model').inputValue(), 'sonnet');
|
||||
await page.locator('[data-runtime-profile-preview]').click();
|
||||
await page.locator('[data-runtime-profile-confirm]').waitFor();
|
||||
await page.locator('[data-runtime-profile-confirm]').click();
|
||||
await page.locator('.agent-row').filter({ hasText: 'interface · claude / sonnet' }).waitFor();
|
||||
|
||||
await page.locator('.approval-decision-form [name="decision"]').selectOption('approved');
|
||||
await page.locator('[data-approval-preview]').click();
|
||||
await page.locator('[data-approval-confirm]').waitFor();
|
||||
await page.locator('[data-approval-confirm]').click();
|
||||
await page.getByText('No open approval requests.', { exact: true }).waitFor();
|
||||
assert.match(await page.locator('#owner-briefing').innerText(), /Choose the project home layout/,
|
||||
'published briefing is historical even when live approval state changes');
|
||||
|
||||
await page.locator('.organization-review > summary').click();
|
||||
await page.locator('[data-organization-review] [name="action"]').selectOption('approve');
|
||||
await page.locator('[data-review-preview]').click();
|
||||
await page.locator('[data-review-confirm]').waitFor();
|
||||
assert.doesNotMatch(await page.locator('[data-review-preview-panel]').innerText(), /undefined/);
|
||||
await page.locator('[data-review-confirm]').click();
|
||||
await page.locator('#organization-action-status').filter({ hasText: 'recorded as approved' }).waitFor();
|
||||
|
||||
mkdirSync('reports/design-system', { recursive: true });
|
||||
await pilotPanel.locator(':scope > summary').click();
|
||||
await pilotPanel.screenshot({ path: 'reports/design-system/torch-dashboard-pilot-review.png' });
|
||||
await page.locator('#manager-wakes').screenshot({ path: 'reports/design-system/torch-dashboard-manager-wakes.png' });
|
||||
await page.evaluate(() => scrollTo(0, 0));
|
||||
await page.screenshot({ path: 'reports/design-system/torch-dashboard-demo-desktop.png' });
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
// Let responsive styles and layout finish before measuring the new viewport.
|
||||
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
await page.evaluate(() => scrollTo(0, 0));
|
||||
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false,
|
||||
JSON.stringify(await page.evaluate(() => ({ viewport: innerWidth, documentWidth: document.documentElement.scrollWidth,
|
||||
bodyWidth: document.body.scrollWidth, overflow: [...document.querySelectorAll('body *')].map((element) => {
|
||||
const bounds = element.getBoundingClientRect();
|
||||
return { tag: element.tagName, id: element.id, className: element.className, right: bounds.right, width: bounds.width };
|
||||
}).filter((element) => element.right > innerWidth + 1).slice(-30) }))));
|
||||
await page.screenshot({ path: 'reports/design-system/torch-dashboard-demo-mobile.png' });
|
||||
await page.locator('.console-rail a[href="#owner-briefing"]').click();
|
||||
const briefingHeading = await page.locator('#owner-briefing-title').boundingBox();
|
||||
const stickyRail = await page.locator('.console-rail').boundingBox();
|
||||
assert.ok(briefingHeading.y >= stickyRail.y + stickyRail.height, 'briefing anchor must clear sticky mobile navigation');
|
||||
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false);
|
||||
await page.locator('#owner-briefing').screenshot({ path: 'reports/design-system/torch-dashboard-owner-briefing-mobile.png' });
|
||||
await page.locator('#owner-briefing .briefing-detail').first().locator('summary').click();
|
||||
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false, 'expanded briefing evidence must fit mobile width');
|
||||
await page.locator('#reset-demo').click();
|
||||
await page.locator('#approval-request-count').filter({ hasText: '1 open' }).waitFor();
|
||||
assert.equal(await page.locator('.priority-urgent').count(), 0);
|
||||
assert.equal(await page.locator('.agent-row').count(), 6);
|
||||
assert.deepEqual(errors, []);
|
||||
const livePage = await context.newPage();
|
||||
await livePage.clock.install({ time: new Date('2026-09-30T12:00:00Z') });
|
||||
await livePage.goto(base + '/console.html?demo=1');
|
||||
await livePage.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
||||
for (const model of ['external-model-1', 'external-model-2']) {
|
||||
await livePage.evaluate(async (model) => {
|
||||
const payload = { areaId: document.querySelector('#runtime-profile-area').value, runtime: 'codex', model, reasoning: 'high' };
|
||||
const preview = await globalThis.TorchConsoleDemo.request('/api/runtime-profiles/preview', payload);
|
||||
await globalThis.TorchConsoleDemo.request('/api/runtime-profiles', { ...payload, token: preview.token, planHash: preview.planHash });
|
||||
}, model);
|
||||
await livePage.locator('#refresh-console').click();
|
||||
assert.equal(await livePage.locator('#runtime-profile-model').inputValue(), model, 'clean editor must track successive external model changes');
|
||||
}
|
||||
await livePage.reload();
|
||||
await livePage.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
||||
await livePage.locator('.runtime-profile-tools > summary').click();
|
||||
await livePage.locator('#runtime-profile-model').fill('owner-draft-model');
|
||||
await livePage.locator('#task-create-form').locator('..').locator(':scope > summary').click();
|
||||
await livePage.locator('#task-create-form [name="title"]').fill('My unfinished task');
|
||||
await livePage.locator('.approval-decision-form [name="decision"]').selectOption('approved');
|
||||
await livePage.locator('[data-approval-preview]').click();
|
||||
await livePage.locator('[data-approval-confirm]').waitFor();
|
||||
const previewToken = await livePage.locator('.approval-decision-form').getAttribute('data-preview-token');
|
||||
await livePage.evaluate(() => { globalThis.oldMessageNode = document.querySelector('#message-list p'); });
|
||||
await livePage.clock.runFor(15_000);
|
||||
await livePage.locator('#live-refresh-status[data-generation="2"]').waitFor();
|
||||
assert.equal(await livePage.locator('#runtime-profile-model').inputValue(), 'owner-draft-model');
|
||||
assert.equal(await livePage.locator('#task-create-form [name="title"]').inputValue(), 'My unfinished task');
|
||||
assert.equal(await livePage.locator('.approval-decision-form').getAttribute('data-preview-token'), previewToken);
|
||||
assert.equal(await livePage.evaluate(() => globalThis.oldMessageNode.isConnected), false, 'unprotected read-only panels must keep updating');
|
||||
assert.match(await livePage.locator('#live-refresh-status').innerText(), /Edited forms and active previews retained/);
|
||||
await livePage.locator('#toggle-live-refresh').click();
|
||||
await livePage.clock.runFor(30_000);
|
||||
assert.equal(await livePage.locator('#live-refresh-status').getAttribute('data-generation'), '2');
|
||||
await livePage.locator('#toggle-live-refresh').click();
|
||||
await livePage.locator('#live-refresh-status[data-generation="3"]').waitFor();
|
||||
await livePage.evaluate(() => Object.defineProperty(document, 'visibilityState', { configurable: true, value: 'hidden' }));
|
||||
await livePage.clock.runFor(30_000);
|
||||
assert.equal(await livePage.locator('#live-refresh-status').getAttribute('data-generation'), '3', 'hidden tabs must not poll');
|
||||
await livePage.evaluate(() => {
|
||||
delete document.visibilityState;
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
});
|
||||
await livePage.locator('#live-refresh-status[data-generation="4"]').waitFor();
|
||||
await livePage.evaluate(() => dispatchEvent(new PageTransitionEvent('pagehide')));
|
||||
await livePage.clock.runFor(30_000);
|
||||
assert.equal(await livePage.locator('#live-refresh-status').getAttribute('data-generation'), '4');
|
||||
await livePage.evaluate(() => dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true })));
|
||||
await livePage.locator('#live-refresh-status[data-generation="5"]').waitFor();
|
||||
await livePage.clock.runFor(15_000);
|
||||
await livePage.locator('#live-refresh-status[data-generation="6"]').waitFor();
|
||||
await livePage.locator('[data-approval-confirm]').click();
|
||||
await livePage.locator('#approval-request-count').filter({ hasText: '0 open' }).waitFor();
|
||||
assert.equal(await livePage.locator('#runtime-profile-model').inputValue(), 'owner-draft-model', 'completing an approval must not clear another form');
|
||||
assert.equal(await livePage.locator('#task-create-form [name="title"]').inputValue(), 'My unfinished task');
|
||||
await livePage.locator('#refresh-console').click();
|
||||
assert.equal(await livePage.locator('#runtime-profile-model').inputValue(), 'owner-draft-model');
|
||||
await livePage.screenshot({ path: 'reports/design-system/torch-dashboard-live-refresh.png' });
|
||||
await livePage.close();
|
||||
assert.deepEqual(errors, []);
|
||||
assert.deepEqual(apiRequests, [], 'demo must never contact live project APIs');
|
||||
console.log('PASS: real Console demo; separate tab; priority, messages, feedback, profiles, approval and organization review; reset; mobile layout; zero live API calls.');
|
||||
} finally {
|
||||
await browser?.close();
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Explicit Linux qualification only; observe, never signal, one owned group."""
|
||||
import json
|
||||
import os
|
||||
import select
|
||||
import sys
|
||||
import time
|
||||
|
||||
root = int(sys.argv[1])
|
||||
expected_start = sys.argv[2]
|
||||
|
||||
|
||||
def record(pid):
|
||||
with open(f"/proc/{pid}/stat", encoding="utf8") as stream:
|
||||
value = stream.read()
|
||||
fields = value[value.rfind(")") + 2:].split()
|
||||
return fields
|
||||
|
||||
|
||||
assert record(root)[19] == expected_start, "Browser PID identity changed"
|
||||
assert os.getpgid(root) == root, "Browser must own an isolated process group"
|
||||
handles = []
|
||||
executor = int(sys.argv[3])
|
||||
assert record(executor)[19] == sys.argv[4], "Harness PID identity changed"
|
||||
handles.append(os.pidfd_open(executor))
|
||||
observed = 0
|
||||
for entry in os.scandir("/proc"):
|
||||
if not entry.name.isdigit():
|
||||
continue
|
||||
try:
|
||||
fields = record(entry.name)
|
||||
if int(fields[2]) != root or fields[0] in ("Z", "X"):
|
||||
continue
|
||||
observed += 1
|
||||
handles.append(os.pidfd_open(int(entry.name)))
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
assert observed > 1, "Qualification must include real browser descendants"
|
||||
print(json.dumps({"event": "watching", "observed": observed}), flush=True)
|
||||
deadline = time.monotonic() + 30
|
||||
remaining = set(handles)
|
||||
while remaining:
|
||||
ready, _, _ = select.select(list(remaining), [], [], max(0, deadline - time.monotonic()))
|
||||
assert ready, "Owned browser group did not reach terminal state"
|
||||
remaining.difference_update(ready)
|
||||
for handle in handles:
|
||||
os.close(handle)
|
||||
for entry in os.scandir("/proc"):
|
||||
if not entry.name.isdigit():
|
||||
continue
|
||||
try:
|
||||
fields = record(entry.name)
|
||||
assert int(fields[2]) != root or fields[0] in ("Z", "X"), "Live group member survived shutdown"
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
print(json.dumps({"event": "terminal", "observed": observed}), flush=True)
|
||||
Reference in new issue
Block a user