mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-29 17:01:55 +00:00
Merge commit '0fdfcb3b68e4511f2993d03e1fff8f89124b731c' into torch/owner-console
This commit is contained in:
commit
b5b5f10cb2
4 files changed
+256
-10
No files matched your search
@@ -2531,3 +2531,31 @@ test_integrity_note:
|
||||
did_relax_any_assertion: false
|
||||
if_true_explain_spec_basis: ""
|
||||
```
|
||||
|
||||
# Standalone dashboard artifact isolation (2026-10-01)
|
||||
|
||||
```yaml
|
||||
test_integrity_note:
|
||||
change_type: new_tests
|
||||
scenarios:
|
||||
- id: SCN-dashboard-artifacts
|
||||
given: "The dashboard test runs without an artifact environment, with an external runner directory, or with a repository/worktree target"
|
||||
when: "It chooses the screenshot output boundary before the browser or screenshot writer starts"
|
||||
then: "The default is a retained fresh external directory with provenance and hashes; explicit external output is honored; repository, temporary-root, pre-existing and symlink-resolved targets fail before protected bytes change"
|
||||
observable_outcomes:
|
||||
- "Printed artifact directory, provenance, relative names, byte counts and SHA-256 hashes"
|
||||
- "Refusal code for direct, temporary-root, existing-file and symlink-resolved tracked-worktree destinations"
|
||||
- "Unchanged tracked baseline and external user bytes after refusal"
|
||||
determinism_controls:
|
||||
- "Disposable Git worktrees and temporary directories"
|
||||
- "Fixed fixture bytes and SHA-256 assertions; no browser, network, sleep or retry is needed for the boundary scenarios"
|
||||
anti_cheat_rationale:
|
||||
prevents:
|
||||
- "Silently falling back to tracked golden-image paths"
|
||||
- "Bypassing repository protection through a symlink"
|
||||
- "Claiming runner output without exact retained evidence provenance"
|
||||
- "Masking a protected-write failure by changing golden assertions"
|
||||
relaxation:
|
||||
did_relax_any_assertion: false
|
||||
if_true_explain_spec_basis: ""
|
||||
```
|
||||
@@ -0,0 +1,120 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { existsSync, lstatSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, realpathSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
||||
|
||||
function isWithin(path, root) {
|
||||
const difference = relative(root, path);
|
||||
return difference === '' || (!isAbsolute(difference) && difference !== '..' && !difference.startsWith(`..${sep}`));
|
||||
}
|
||||
|
||||
function existingAncestor(path) {
|
||||
let ancestor = resolve(path);
|
||||
while (!existsSync(ancestor)) {
|
||||
const parent = dirname(ancestor);
|
||||
if (parent === ancestor) throw new Error(`No existing ancestor for ${path}`);
|
||||
ancestor = parent;
|
||||
}
|
||||
return ancestor;
|
||||
}
|
||||
|
||||
function trackedWorktreeRoots(repositoryRoot) {
|
||||
const roots = new Set([resolve(repositoryRoot)]);
|
||||
const output = execFileSync('git', ['worktree', 'list', '--porcelain'], {
|
||||
cwd: repositoryRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
for (const line of output.split('\n')) {
|
||||
if (line.startsWith('worktree ')) roots.add(resolve(line.slice('worktree '.length)));
|
||||
}
|
||||
return [...roots].flatMap((root) => existsSync(root) ? [root, realpathSync(root)] : [root]);
|
||||
}
|
||||
|
||||
function refusal(target, protectedRoot) {
|
||||
const error = new Error(`DASHBOARD_ARTIFACT_DIRECTORY_FORBIDDEN: ${target} resolves into tracked worktree ${protectedRoot}`);
|
||||
error.code = 'DASHBOARD_ARTIFACT_DIRECTORY_FORBIDDEN';
|
||||
return error;
|
||||
}
|
||||
|
||||
function targetExists(target) {
|
||||
try {
|
||||
lstatSync(target);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') return false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePhysicalDirectory(directory) {
|
||||
const requested = resolve(directory);
|
||||
const ancestor = existingAncestor(requested);
|
||||
return { requested, resolved: resolve(realpathSync(ancestor), relative(ancestor, requested)) };
|
||||
}
|
||||
|
||||
function assertExternalDirectory({ requested, resolved }, repositoryRoot) {
|
||||
for (const protectedRoot of trackedWorktreeRoots(repositoryRoot)) {
|
||||
if (isWithin(resolved, protectedRoot)) throw refusal(requested, protectedRoot);
|
||||
}
|
||||
}
|
||||
|
||||
export function createDashboardArtifactDirectory({
|
||||
environment = process.env,
|
||||
repositoryRoot,
|
||||
temporaryRoot = tmpdir(),
|
||||
} = {}) {
|
||||
if (!repositoryRoot) throw new Error('repositoryRoot is required for dashboard artifact isolation');
|
||||
const requestedDirectory = environment.TORCH_CHECK_ARTIFACT_DIR;
|
||||
if (!requestedDirectory) {
|
||||
const temporary = resolvePhysicalDirectory(temporaryRoot);
|
||||
assertExternalDirectory(temporary, repositoryRoot);
|
||||
const directory = mkdtempSync(join(temporary.resolved, 'torch-dashboard-artifacts-'));
|
||||
return { directory, provenance: 'standalone-temporary', requestedDirectory: null };
|
||||
}
|
||||
|
||||
const output = resolvePhysicalDirectory(requestedDirectory);
|
||||
assertExternalDirectory(output, repositoryRoot);
|
||||
mkdirSync(output.resolved, { recursive: true });
|
||||
return { directory: output.resolved, provenance: 'TORCH_CHECK_ARTIFACT_DIR', requestedDirectory: output.requested };
|
||||
}
|
||||
|
||||
export function prepareDashboardArtifactTargets(artifacts, names) {
|
||||
if (new Set(names).size !== names.length || names.some((name) => name !== basename(name))) {
|
||||
throw new Error('Dashboard screenshot names must be unique plain filenames');
|
||||
}
|
||||
const paths = {};
|
||||
for (const name of names) {
|
||||
const target = join(artifacts.directory, name);
|
||||
if (targetExists(target)) {
|
||||
const error = new Error(`DASHBOARD_ARTIFACT_TARGET_EXISTS: refusing to replace ${target}`);
|
||||
error.code = 'DASHBOARD_ARTIFACT_TARGET_EXISTS';
|
||||
throw error;
|
||||
}
|
||||
paths[name] = target;
|
||||
}
|
||||
return Object.freeze(paths);
|
||||
}
|
||||
|
||||
function artifactFiles(directory, prefix = '') {
|
||||
return readdirSync(directory, { withFileTypes: true })
|
||||
.flatMap((entry) => {
|
||||
const path = join(directory, entry.name);
|
||||
const relativePath = join(prefix, entry.name);
|
||||
if (entry.isDirectory()) return artifactFiles(path, relativePath);
|
||||
if (!entry.isFile()) return [];
|
||||
const bytes = readFileSync(path);
|
||||
return [{ path: relativePath, bytes: bytes.length,
|
||||
sha256: createHash('sha256').update(bytes).digest('hex') }];
|
||||
})
|
||||
.sort((left, right) => left.path.localeCompare(right.path));
|
||||
}
|
||||
|
||||
export function describeDashboardArtifacts(artifacts) {
|
||||
return {
|
||||
kind: 'dashboard-artifacts',
|
||||
directory: artifacts.directory,
|
||||
provenance: artifacts.provenance,
|
||||
requestedDirectory: artifacts.requestedDirectory,
|
||||
files: artifactFiles(artifacts.directory),
|
||||
};
|
||||
}
|
||||
@@ -1,13 +1,24 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { once } from 'node:events';
|
||||
import { chromium } from '@playwright/test';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createConsoleServer } from '../src/console/server.mjs';
|
||||
import { createDashboardArtifactDirectory, describeDashboardArtifacts, prepareDashboardArtifactTargets } from './dashboard-artifacts.mjs';
|
||||
|
||||
const screenshotDirectory = process.env.TORCH_CHECK_ARTIFACT_DIR || 'reports/design-system';
|
||||
const screenshotNames = [
|
||||
'torch-dashboard-operation-outcomes.png', 'torch-dashboard-activity-review.png',
|
||||
'torch-dashboard-check-evidence.png', 'torch-dashboard-owner-briefing-desktop.png',
|
||||
'torch-dashboard-pilot-conclusion.png', 'torch-dashboard-pilot-review.png',
|
||||
'torch-dashboard-manager-wakes.png', 'torch-dashboard-demo-desktop.png',
|
||||
'torch-dashboard-demo-mobile.png', 'torch-dashboard-owner-briefing-mobile.png',
|
||||
'torch-dashboard-live-refresh.png',
|
||||
];
|
||||
const artifacts = createDashboardArtifactDirectory({
|
||||
repositoryRoot: fileURLToPath(new URL('..', import.meta.url)),
|
||||
});
|
||||
const screenshotPaths = prepareDashboardArtifactTargets(artifacts, screenshotNames);
|
||||
function screenshotPath(name) {
|
||||
return join(screenshotDirectory, name);
|
||||
return screenshotPaths[name];
|
||||
}
|
||||
|
||||
const server = createConsoleServer();
|
||||
@@ -41,12 +52,10 @@ try {
|
||||
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(screenshotDirectory, { recursive: true });
|
||||
await page.locator('#operation-outcomes').screenshot({ path: screenshotPath('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(screenshotDirectory, { recursive: true });
|
||||
await page.locator('#activity-review').screenshot({ path: screenshotPath('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();
|
||||
@@ -57,13 +66,11 @@ try {
|
||||
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(screenshotDirectory, { recursive: true });
|
||||
await page.locator('#check-list').screenshot({ path: screenshotPath('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(screenshotDirectory, { recursive: true });
|
||||
await page.locator('#owner-briefing').screenshot({ path: screenshotPath('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();
|
||||
@@ -99,7 +106,6 @@ try {
|
||||
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(screenshotDirectory, { recursive: true });
|
||||
await conclusion.screenshot({ path: screenshotPath('torch-dashboard-pilot-conclusion.png') });
|
||||
await conclusion.locator('[data-review-confirm]').click();
|
||||
await page.locator('#organization-action-status').filter({ hasText: 'recorded as reversed' }).waitFor();
|
||||
@@ -155,7 +161,6 @@ try {
|
||||
await page.locator('[data-review-confirm]').click();
|
||||
await page.locator('#organization-action-status').filter({ hasText: 'recorded as approved' }).waitFor();
|
||||
|
||||
mkdirSync(screenshotDirectory, { recursive: true });
|
||||
await pilotPanel.locator(':scope > summary').click();
|
||||
await pilotPanel.screenshot({ path: screenshotPath('torch-dashboard-pilot-review.png') });
|
||||
await page.locator('#manager-wakes').screenshot({ path: screenshotPath('torch-dashboard-manager-wakes.png') });
|
||||
@@ -250,4 +255,5 @@ try {
|
||||
} finally {
|
||||
await browser?.close();
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
console.log(JSON.stringify(describeDashboardArtifacts(artifacts)));
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { existsSync, mkdtempSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { createDashboardArtifactDirectory, describeDashboardArtifacts, prepareDashboardArtifactTargets } from '../../scripts/dashboard-artifacts.mjs';
|
||||
|
||||
function repositoryFixture() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'torch-dashboard-artifact-fixture-'));
|
||||
execFileSync('git', ['init', '-b', 'main', root]);
|
||||
execFileSync('git', ['-C', root, 'config', 'user.email', '[email protected]']);
|
||||
execFileSync('git', ['-C', root, 'config', 'user.name', 'TORCH Test']);
|
||||
mkdirSync(join(root, 'reports', 'design-system'), { recursive: true });
|
||||
writeFileSync(join(root, 'reports', 'design-system', 'baseline.png'), 'tracked baseline bytes');
|
||||
execFileSync('git', ['-C', root, 'add', '.']);
|
||||
execFileSync('git', ['-C', root, 'commit', '-m', 'dashboard baseline']);
|
||||
return root;
|
||||
}
|
||||
|
||||
test('SCN-dashboard-artifacts: standalone execution retains fresh external screenshot evidence with provenance', () => {
|
||||
const repositoryRoot = repositoryFixture();
|
||||
const artifacts = createDashboardArtifactDirectory({ environment: {}, repositoryRoot });
|
||||
assert.equal(artifacts.provenance, 'standalone-temporary');
|
||||
assert.equal(artifacts.requestedDirectory, null);
|
||||
assert.equal(artifacts.directory.startsWith(resolve(repositoryRoot)), false);
|
||||
const targets = prepareDashboardArtifactTargets(artifacts, ['captured.png']);
|
||||
writeFileSync(targets['captured.png'], 'standalone screenshot');
|
||||
assert.deepEqual(describeDashboardArtifacts(artifacts).files, [{
|
||||
path: 'captured.png', bytes: 21,
|
||||
sha256: '6921ad2c105108a005db4282443624252c392392d85bcb4ca2625c6af488f57c',
|
||||
}]);
|
||||
});
|
||||
|
||||
test('SCN-dashboard-artifacts: an explicit external runner directory receives hashable captured evidence', () => {
|
||||
const repositoryRoot = repositoryFixture();
|
||||
const output = mkdtempSync(join(tmpdir(), 'torch-dashboard-artifact-output-'));
|
||||
const artifacts = createDashboardArtifactDirectory({
|
||||
environment: { TORCH_CHECK_ARTIFACT_DIR: output }, repositoryRoot,
|
||||
});
|
||||
assert.equal(artifacts.provenance, 'TORCH_CHECK_ARTIFACT_DIR');
|
||||
assert.equal(artifacts.directory, resolve(output));
|
||||
const targets = prepareDashboardArtifactTargets(artifacts, ['captured.png']);
|
||||
writeFileSync(targets['captured.png'], 'runner screenshot');
|
||||
assert.deepEqual(describeDashboardArtifacts(artifacts).files, [{
|
||||
path: 'captured.png', bytes: 17,
|
||||
sha256: '382d6e41938a7a8cc7767e9b519ddbef25e5048fc6fa9fe02faf31bab4148e2b',
|
||||
}]);
|
||||
});
|
||||
|
||||
test('SCN-dashboard-artifacts: repository, temporary-root, pre-existing and symlink targets fail before changing preserved bytes', () => {
|
||||
const repositoryRoot = repositoryFixture();
|
||||
const baseline = join(repositoryRoot, 'reports', 'design-system', 'baseline.png');
|
||||
const before = readFileSync(baseline);
|
||||
for (const direct of [join(repositoryRoot, 'reports', 'design-system'), join(repositoryRoot, '..captures')]) {
|
||||
assert.throws(() => createDashboardArtifactDirectory({
|
||||
environment: { TORCH_CHECK_ARTIFACT_DIR: direct }, repositoryRoot,
|
||||
}), (error) => error.code === 'DASHBOARD_ARTIFACT_DIRECTORY_FORBIDDEN');
|
||||
}
|
||||
assert.throws(() => createDashboardArtifactDirectory({
|
||||
environment: {}, repositoryRoot, temporaryRoot: join(repositoryRoot, 'unsafe-tmp'),
|
||||
}), (error) => error.code === 'DASHBOARD_ARTIFACT_DIRECTORY_FORBIDDEN');
|
||||
assert.deepEqual(readFileSync(baseline), before);
|
||||
|
||||
const linkedWorktree = mkdtempSync(join(tmpdir(), 'torch-dashboard-linked-worktree-'));
|
||||
execFileSync('git', ['-C', repositoryRoot, 'worktree', 'add', '-b', 'linked-dashboard-fixture', linkedWorktree]);
|
||||
const link = join(tmpdir(), `torch-dashboard-worktree-link-${process.pid}-${Date.now()}`);
|
||||
symlinkSync(join(linkedWorktree, 'reports', 'design-system'), link);
|
||||
assert.throws(() => createDashboardArtifactDirectory({
|
||||
environment: { TORCH_CHECK_ARTIFACT_DIR: link }, repositoryRoot,
|
||||
}), (error) => error.code === 'DASHBOARD_ARTIFACT_DIRECTORY_FORBIDDEN');
|
||||
assert.deepEqual(readFileSync(baseline), before);
|
||||
assert.equal(existsSync(join(linkedWorktree, 'reports', 'design-system', 'torch-dashboard-demo-desktop.png')), false);
|
||||
|
||||
const output = mkdtempSync(join(tmpdir(), 'torch-dashboard-existing-output-'));
|
||||
const expected = join(output, 'torch-dashboard-demo-desktop.png');
|
||||
writeFileSync(expected, 'existing user evidence');
|
||||
const external = createDashboardArtifactDirectory({ environment: { TORCH_CHECK_ARTIFACT_DIR: output }, repositoryRoot });
|
||||
assert.throws(() => prepareDashboardArtifactTargets(external, ['torch-dashboard-demo-desktop.png']),
|
||||
(error) => error.code === 'DASHBOARD_ARTIFACT_TARGET_EXISTS');
|
||||
assert.equal(readFileSync(expected, 'utf8'), 'existing user evidence');
|
||||
|
||||
const linkedOutput = mkdtempSync(join(tmpdir(), 'torch-dashboard-linked-output-'));
|
||||
const linkedExpected = join(linkedOutput, 'torch-dashboard-demo-desktop.png');
|
||||
symlinkSync(baseline, linkedExpected);
|
||||
const linkedExternal = createDashboardArtifactDirectory({
|
||||
environment: { TORCH_CHECK_ARTIFACT_DIR: linkedOutput }, repositoryRoot,
|
||||
});
|
||||
assert.throws(() => prepareDashboardArtifactTargets(linkedExternal, ['torch-dashboard-demo-desktop.png']),
|
||||
(error) => error.code === 'DASHBOARD_ARTIFACT_TARGET_EXISTS');
|
||||
assert.deepEqual(readFileSync(baseline), before);
|
||||
});
|
||||
Reference in new issue
Block a user