mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
Record final-content browser proof for the composed frontend
Verify the 26 missing final contents across inherited and newly composed PBS/NAS, guest backup, notification, Docker and History surfaces. Preserve valid old receipts; add one content-fingerprinted own-parent receipt without changing application source. Keep a production backup-page harness with measured desktop/phone CSS viewports, independent backup versus snapshot attribution, location filtering and rendered recovery waits. Change-source: pulse-maintainer Contract-Neutral: Browser proof and verification-harness changes only; no application, API, graph, safety or subsystem-contract delta.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
// Production orchestration/table/CSS with synthetic read-only HTTP evidence.
|
||||
// Confirms combined Tailwind rendering and source attribution, not native backup recovery.
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
const { chromium, webkit } = require('playwright');
|
||||
(async () => {
|
||||
const root = '/workspace/frontend-modern';
|
||||
const output = '/workspace/tmp/composed-backups-proof';
|
||||
fs.mkdirSync(output, { recursive: true });
|
||||
process.chdir(root);
|
||||
const playwright = require('playwright/package.json').version;
|
||||
assert.equal(
|
||||
playwright,
|
||||
JSON.parse(fs.readFileSync('/workspace/tests/integration/package-lock.json')).packages[
|
||||
'node_modules/@playwright/test'
|
||||
].version,
|
||||
);
|
||||
const { createServer } = await import(path.join(root, 'node_modules/vite/dist/node/index.js'));
|
||||
const server = await createServer({
|
||||
root,
|
||||
configFile: path.join(root, 'vite.config.ts'),
|
||||
cacheDir: path.join(output, 'vite-cache'),
|
||||
server: { host: '127.0.0.1', port: 5295, strictPort: true },
|
||||
});
|
||||
const runtime = [
|
||||
'frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx',
|
||||
'frontend-modern/src/features/proxmox/proxmoxBackupsTableShared.tsx',
|
||||
];
|
||||
const content_sha256 = Object.fromEntries(
|
||||
runtime.map((p) => [
|
||||
p,
|
||||
crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(path.join('/workspace', p)))
|
||||
.digest('hex'),
|
||||
]),
|
||||
);
|
||||
const observations = [];
|
||||
let browser;
|
||||
try {
|
||||
await server.listen();
|
||||
for (const [name, engine, phone] of [
|
||||
['chromium-desktop', chromium, false],
|
||||
['webkit-phone', webkit, true],
|
||||
]) {
|
||||
browser = await engine.launch(
|
||||
engine === chromium
|
||||
? { headless: true, channel: 'chromium', args: ['--no-sandbox'] }
|
||||
: { headless: true },
|
||||
);
|
||||
const page = await browser.newPage({
|
||||
viewport: phone ? { width: 390, height: 844 } : { width: 1365, height: 900 },
|
||||
hasTouch: phone,
|
||||
isMobile: phone,
|
||||
locale: 'en-GB',
|
||||
timezoneId: 'UTC',
|
||||
});
|
||||
page.setDefaultTimeout(20000);
|
||||
await page.clock.setFixedTime(new Date('2026-10-03T12:00:00Z'));
|
||||
const errors = [],
|
||||
requests = [],
|
||||
offOrigin = [],
|
||||
checks = [],
|
||||
screenshots = [];
|
||||
const now = '2026-10-03T12:00:00Z';
|
||||
const workload = {
|
||||
id: 'ct-310',
|
||||
type: 'system-container',
|
||||
name: 'artifact-cache-310',
|
||||
displayName: 'artifact-cache-310',
|
||||
platformId: 'fixture-estate',
|
||||
platformType: 'proxmox-pve',
|
||||
sourceType: 'api',
|
||||
status: 'running',
|
||||
lastSeen: now,
|
||||
proxmox: { vmid: 310, node: 'pve-edge', instance: 'homelab' },
|
||||
};
|
||||
const archive = {
|
||||
id: 'file-310',
|
||||
storage: 'local',
|
||||
node: 'pve-edge',
|
||||
instance: 'homelab',
|
||||
type: 'ct',
|
||||
vmid: 310,
|
||||
time: '2026-09-20T02:00:00Z',
|
||||
ctime: Date.parse('2026-09-20T02:00:00Z') / 1000,
|
||||
size: 1048576,
|
||||
format: 'tar.zst',
|
||||
protected: false,
|
||||
volid: 'local:backup/vzdump-lxc-310-2026_09_20-02_00_00.tar.zst',
|
||||
isPBS: false,
|
||||
verified: false,
|
||||
};
|
||||
const snapshot = {
|
||||
id: 'snap-310',
|
||||
name: 'fresh-local-snapshot',
|
||||
node: 'pve-edge',
|
||||
instance: 'homelab',
|
||||
type: 'ct',
|
||||
vmid: 310,
|
||||
time: '2026-10-03T11:30:00Z',
|
||||
vmstate: false,
|
||||
};
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
await page.route('**/*', (route) => {
|
||||
const request = route.request(),
|
||||
url = new URL(request.url());
|
||||
if (url.origin !== 'http://127.0.0.1:5295') {
|
||||
offOrigin.push(url.origin);
|
||||
return route.abort();
|
||||
}
|
||||
if (url.pathname === '/fixture/workloads') return route.fulfill({ json: [workload] });
|
||||
if (!url.pathname.startsWith('/api/')) return route.continue();
|
||||
requests.push({ path: url.pathname, method: request.method() });
|
||||
assert.equal(request.method(), 'GET');
|
||||
if (url.pathname === '/api/backups/pve')
|
||||
return route.fulfill({
|
||||
json: {
|
||||
data: { backupTasks: [], storageBackups: [archive], guestSnapshots: [snapshot] },
|
||||
},
|
||||
});
|
||||
if (url.pathname === '/api/backups/pbs')
|
||||
return route.fulfill({ json: { data: { backups: [] } } });
|
||||
if (url.pathname === '/api/recovery/postures')
|
||||
return route.fulfill({
|
||||
json: {
|
||||
data: [],
|
||||
policy: {
|
||||
freshnessWindowSeconds: 0,
|
||||
verificationWindowSeconds: 0,
|
||||
requireVerification: false,
|
||||
},
|
||||
meta: { page: 1, limit: 200, total: 0, totalPages: 0 },
|
||||
},
|
||||
});
|
||||
return route.fulfill({
|
||||
json:
|
||||
url.pathname === '/api/license/runtime-capabilities'
|
||||
? {
|
||||
capabilities: [],
|
||||
limits: [],
|
||||
max_history_days: 7,
|
||||
hosted_mode: false,
|
||||
runtime: { build: 'community' },
|
||||
blocked_capabilities: [],
|
||||
}
|
||||
: { data: [], enabled: false },
|
||||
});
|
||||
});
|
||||
try {
|
||||
await page.goto('http://127.0.0.1:5295/browser-tests/backups.html', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 120000,
|
||||
});
|
||||
if (phone) await page.evaluate(() => document.documentElement.classList.add('dark'));
|
||||
const capture = async (state) => {
|
||||
await page.waitForTimeout(100);
|
||||
const dimensions = await page.evaluate(() => ({
|
||||
width: innerWidth,
|
||||
scroll: document.documentElement.scrollWidth,
|
||||
}));
|
||||
assert.equal(
|
||||
dimensions.width,
|
||||
phone ? 390 : 1365,
|
||||
'actual CSS viewport, not only Playwright emulation',
|
||||
);
|
||||
assert.ok(dimensions.scroll <= dimensions.width + 1, JSON.stringify(dimensions));
|
||||
const file = `${name}-${state}.png`;
|
||||
await page.screenshot({ path: path.join(output, file), fullPage: true });
|
||||
screenshots.push({ file, dimensions });
|
||||
};
|
||||
await page.getByRole('link', { name: 'By date', exact: true }).click();
|
||||
const byDate = page.locator('[data-proxmox-backups-table="recoverable"]');
|
||||
await byDate.getByText('artifact-cache-310', { exact: true }).first().waitFor();
|
||||
await capture('by-date-diagnostic');
|
||||
fs.writeFileSync(
|
||||
path.join(output, `${name}-by-date-diagnostic.json`),
|
||||
JSON.stringify({ text: await byDate.innerText(), requests, errors }, null, 2),
|
||||
);
|
||||
if (!phone) {
|
||||
await byDate.getByText('tar.zst', { exact: true }).waitFor();
|
||||
assert.ok(await byDate.locator(`[title="${archive.volid}"]`).count());
|
||||
}
|
||||
checks.push(
|
||||
'Explicit by-date navigation retains source, archive format and complete wide identifier',
|
||||
);
|
||||
await capture('by-date');
|
||||
await page.getByRole('link', { name: 'Coverage', exact: true }).click();
|
||||
const coverage = page.locator('[data-proxmox-backups-table="coverage"]');
|
||||
await coverage.getByText('artifact-cache-310', { exact: true }).waitFor();
|
||||
const columns = await coverage
|
||||
.locator('colgroup col')
|
||||
.evaluateAll((elements) => elements.map((e) => e.dataset.proxmoxBackupsColumn));
|
||||
const row = coverage.locator('tr[data-proxmox-backup-row="coverage"]').first();
|
||||
const backupAge = await row.locator('td').nth(columns.indexOf('latest')).innerText();
|
||||
assert.ok(backupAge.includes('13d'), backupAge);
|
||||
assert.ok(!backupAge.includes('30m'), backupAge);
|
||||
if (columns.includes('snapshot'))
|
||||
assert.ok(
|
||||
(await row.locator('td').nth(columns.indexOf('snapshot')).innerText()).includes('30m'),
|
||||
);
|
||||
checks.push('Fresh local guest snapshot does not masquerade as a recent backup');
|
||||
await capture('coverage');
|
||||
const search = page.getByPlaceholder('Search backups by workload, node, source, or status');
|
||||
await search.fill('non-existent-fixture');
|
||||
await page
|
||||
.getByText('No workload coverage rows match current filters', { exact: true })
|
||||
.waitFor();
|
||||
await search.fill('artifact-cache');
|
||||
await coverage.getByText('artifact-cache-310', { exact: true }).waitFor();
|
||||
checks.push('Coverage route query filters and recovery preserve the target');
|
||||
if (phone && !(await page.getByRole('combobox', { name: 'Filter', exact: true }).count()))
|
||||
await page.getByRole('button', { name: /^Filters/ }).click();
|
||||
await page
|
||||
.getByRole('combobox', { name: 'Filter', exact: true })
|
||||
.selectOption({ label: 'Backup location: homelab / pve-edge' });
|
||||
await page.waitForFunction(
|
||||
() => new URL(location.href).searchParams.get('location') === 'snapshot:homelab:pve-edge',
|
||||
);
|
||||
const scopedColumns = await coverage
|
||||
.locator('colgroup col')
|
||||
.evaluateAll((elements) => elements.map((e) => e.dataset.proxmoxBackupsColumn));
|
||||
const scopedAge = row.locator('td').nth(scopedColumns.indexOf('latest'));
|
||||
await scopedAge.getByText('None', { exact: true }).waitFor();
|
||||
assert.ok(
|
||||
(await scopedAge.locator('span[title]').getAttribute('title')).includes(
|
||||
'Guest snapshots alone do not prove independent recovery.',
|
||||
),
|
||||
);
|
||||
checks.push(
|
||||
'Snapshot-only location scope reports no independent backup, not a fresh restore promise',
|
||||
);
|
||||
await capture('snapshot-only-location');
|
||||
if (
|
||||
phone &&
|
||||
!(await page
|
||||
.getByRole('button', { name: 'Remove Backup location filter', exact: true })
|
||||
.count())
|
||||
)
|
||||
await page.getByRole('button', { name: /^Filters/ }).click();
|
||||
await page
|
||||
.getByRole('button', { name: 'Remove Backup location filter', exact: true })
|
||||
.click();
|
||||
await page.waitForFunction(() => !new URL(location.href).searchParams.has('location'));
|
||||
await row
|
||||
.locator('td')
|
||||
.nth(columns.indexOf('latest'))
|
||||
.getByText(phone ? '13d' : '13d ago', { exact: true })
|
||||
.waitFor();
|
||||
checks.push('Removing location scope restores the independently dated archive');
|
||||
assert.deepEqual(errors, []);
|
||||
assert.deepEqual(offOrigin, []);
|
||||
assert.ok(
|
||||
requests.some((r) => r.path === '/api/backups/pve') &&
|
||||
requests.some((r) => r.path === '/api/backups/pbs'),
|
||||
);
|
||||
observations.push({
|
||||
name,
|
||||
browser: browser.version(),
|
||||
viewport: page.viewportSize(),
|
||||
checks,
|
||||
requests,
|
||||
errors,
|
||||
offOrigin,
|
||||
screenshots,
|
||||
});
|
||||
} catch (error) {
|
||||
fs.writeFileSync(
|
||||
path.join(output, `${name}-failure.json`),
|
||||
JSON.stringify(
|
||||
{
|
||||
error: error.message,
|
||||
text: await page.locator('body').innerText(),
|
||||
url: page.url(),
|
||||
requests,
|
||||
errors,
|
||||
offOrigin,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
await page.screenshot({ path: path.join(output, `${name}-failure.png`), fullPage: true });
|
||||
throw error;
|
||||
}
|
||||
await browser.close();
|
||||
browser = null;
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(output, 'result.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
passed: true,
|
||||
playwright,
|
||||
content_sha256,
|
||||
harness_sha256: crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(__filename))
|
||||
.digest('hex'),
|
||||
fixture_sha256: Object.fromEntries(
|
||||
['backups.html', 'backups.tsx'].map((file) => [
|
||||
file,
|
||||
crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(path.join(root, 'browser-tests', file)))
|
||||
.digest('hex'),
|
||||
]),
|
||||
),
|
||||
observations,
|
||||
limitation:
|
||||
'Synthetic production component rendering, not native backup, QGA/thaw, installation or shipment.',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
passed: true,
|
||||
browsers: observations.length,
|
||||
checks: observations.reduce((n, o) => n + o.checks.length, 0),
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
if (browser) await browser.close();
|
||||
await server.close();
|
||||
}
|
||||
})().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"version": 1,
|
||||
"result": "passed",
|
||||
"base_sha": "50756d745ea7ae62d3e1bbdccd03a03ce8963a1a",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailSummary.tsx",
|
||||
"frontend-modern/src/components/Storage/EnhancedStorageBar.tsx",
|
||||
"frontend-modern/src/components/Storage/StoragePoolsTable.tsx",
|
||||
"frontend-modern/src/components/Workloads/DiskList.tsx",
|
||||
"frontend-modern/src/components/Workloads/GuestDrawerHistory.tsx",
|
||||
"frontend-modern/src/components/Workloads/GuestRow.tsx",
|
||||
"frontend-modern/src/components/Workloads/StackedDiskBar.tsx",
|
||||
"frontend-modern/src/components/shared/HistoryChartOverlay.tsx",
|
||||
"frontend-modern/src/features/alerts/AlertDeliveryLogCard.tsx",
|
||||
"frontend-modern/src/features/docker/DockerServicesTable.tsx",
|
||||
"frontend-modern/src/features/docker/DockerTasksTable.tsx",
|
||||
"frontend-modern/src/features/kubernetes/KubernetesControllersTable.tsx",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxBackupServersTable.tsx",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxCoverageTable.tsx",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxRecoverableTable.tsx",
|
||||
"frontend-modern/src/features/proxmox/proxmoxBackupsTableShared.tsx",
|
||||
"frontend-modern/src/features/proxmox/proxmoxHostTableModel.ts",
|
||||
"frontend-modern/src/features/standalone/AvailabilityFleetView.tsx",
|
||||
"frontend-modern/src/features/storageBackups/diskDetailPresentation.ts",
|
||||
"frontend-modern/src/features/storageBackups/diskPresentation.ts",
|
||||
"frontend-modern/src/features/storageBackups/storagePagePresentation.ts",
|
||||
"frontend-modern/src/utils/workloadGuestPresentation.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/components/Infrastructure/ResourceActionHistory.tsx": "4fc443b465a7fe78df9f2b3faad6337bdddd118f9cfd33faa9217aaeb2d43c1b",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawer.tsx": "b922b64a4388855c29296b178df553c60ba9395bd63d1433f79c6112030c8399",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx": "aca82b1f7a6f330d1f6fd7bdb6301420d3a7bb4f7f22fa5666e013a8a656ed26",
|
||||
"frontend-modern/src/components/Infrastructure/ResourceDetailSummary.tsx": "ddad24a010a2a194f37977f3c3aa3fa647b431bd65d1d75e9f5aea6d42f72692",
|
||||
"frontend-modern/src/components/Storage/EnhancedStorageBar.tsx": "8021e55b50564fc9dfa29ebb83a824541b3ba34a1a6fc0fcc3793af5e5e4f701",
|
||||
"frontend-modern/src/components/Storage/StoragePoolsTable.tsx": "abab4dcd8a67c39a846a97e46d3ba4e910b577f84318ed1e9c6a2c1cb6dc81fa",
|
||||
"frontend-modern/src/components/Workloads/DiskList.tsx": "b9828bd9a9593093cfaeee5816a644b22177c0e91146c1592e01149a1c833793",
|
||||
"frontend-modern/src/components/Workloads/GuestDrawerHistory.tsx": "83a0d0c00786ab3a9115d0534a6d5a08f3c428109469a932c7d4f7fd9accc2b4",
|
||||
"frontend-modern/src/components/Workloads/GuestRow.tsx": "33ce83ef5a529cbab740326de6c6b04ac62ca2024f07f3414262f68c69727186",
|
||||
"frontend-modern/src/components/Workloads/StackedDiskBar.tsx": "ff67b81bc5722f27f49820aeed76e5cc20b770cbd2fae4dbd1f267b10519995d",
|
||||
"frontend-modern/src/components/shared/HistoryChartOverlay.tsx": "a41425ff8dc2cd0768c7a9bf2f1192a3a058498d002ba3e8723979c5aaa60950",
|
||||
"frontend-modern/src/features/alerts/AlertDeliveryLogCard.tsx": "75530d5d16a579d415a5bc2c9f86e507555de1446d06529315b6c31919b03e03",
|
||||
"frontend-modern/src/features/docker/DockerServicesTable.tsx": "a6d61e02ed31912abe9d1a33776a923412bef9aaa03b44d84facee723e0cc190",
|
||||
"frontend-modern/src/features/docker/DockerTasksTable.tsx": "6cc99405c7bdb2b2edfd992f6b17b2636bb9285a53e949e41609dfa8b339cacf",
|
||||
"frontend-modern/src/features/kubernetes/KubernetesControllersTable.tsx": "5d7362905632cf4e69c4a878f972ac40544bda2959db2822a431bcc74ba35089",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxBackupServersTable.tsx": "b791a50845befbdcfdca979355188fb1e977878ce69e8a72cbbdf90fda5e8cbe",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxBackupsTable.tsx": "97a95f97c94795005300fe55fdca81756b6ce9f11e977d1a3f683775eb0dc88d",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxCoverageTable.tsx": "f85558ab1092078b9a3d160c986450d2167552b10b2f76c10087fceef5af0689",
|
||||
"frontend-modern/src/features/proxmox/ProxmoxRecoverableTable.tsx": "1dc95dd84f499e38a8fca77650ae72ff6c903b7c03c8fdc3cf366b309817e488",
|
||||
"frontend-modern/src/features/proxmox/proxmoxBackupsTableShared.tsx": "80079e8de47bbaa368ff511445209fe61fdbcd7ac0ce8003ef7d90ab338df2de",
|
||||
"frontend-modern/src/features/proxmox/proxmoxHostTableModel.ts": "908af893071386d1a11a7191694b9fef399e4b134e78188bee90faa80f758e91",
|
||||
"frontend-modern/src/features/standalone/AvailabilityFleetView.tsx": "426d43f1635b659949e2317740ca09b54dfff89b1cd93c4dc6035a6b82fc2d13",
|
||||
"frontend-modern/src/features/storageBackups/diskDetailPresentation.ts": "90670aaa66ce08fb90570b757029e10ea232c21f2e64ea4ce5e6cc3daa829d41",
|
||||
"frontend-modern/src/features/storageBackups/diskPresentation.ts": "6e1faea3be9f1c7bacc8bc74ee03b2849c0e254bdee29cf6fb912c90d0ae6623",
|
||||
"frontend-modern/src/features/storageBackups/storagePagePresentation.ts": "51a917ac758ce2611f444d6c37240de1e706282adad81f1feaa621aee939d115",
|
||||
"frontend-modern/src/utils/workloadGuestPresentation.ts": "c51181ed80bc33907d331f2fd646b5aa209ebde622fb1ec1b6b70b8dcc2fd7d8"
|
||||
},
|
||||
"routes": [
|
||||
"/browser-tests/guest-disk-deferral.html",
|
||||
"/browser-tests/proxmox-docker-rows.html",
|
||||
"/browser-tests/operator-tables-consent.html",
|
||||
"/browser-tests/notification-evidence.html",
|
||||
"/browser-tests/resource-evidence-access.html",
|
||||
"/browser-tests/history-access.html",
|
||||
"/browser-tests/history-keyboard.html",
|
||||
"/browser-tests/drawer-snapshot.html",
|
||||
"/browser-tests/backups.html"
|
||||
],
|
||||
"states": [
|
||||
"Eight guest disk deferral/uncertainty families with and without retained observations; seven distinct unavailable states; fresh same-VM resumption",
|
||||
"PBS/NAS pool health and capacity, full SMART reason, Kubernetes absolute started/completed and last-run context",
|
||||
"REST-mapped Docker/PMG/storage rows and disclosures; fresh and missing facets; Swarm lifecycle details",
|
||||
"Backup age, source and archive detail remain independent of guest snapshots, including snapshot-only repository scope",
|
||||
"Notification initial/held pending, denied/unavailable and retained-transient read states",
|
||||
"History/action denial withdraws prior samples, cache and fallback; transient failures retain truthful last-known evidence; pending retry and remount",
|
||||
"Same-ID drawer replacement, target withdrawal, explicit selection and removed/restored availability checks",
|
||||
"History overlay keyboard focus, arrow/Home/End bounds, grouped tooltips, Escape and empty data",
|
||||
"Rollback consent reports unknown restore scope; rejection keeps consent and pending submissions cannot be duplicated or cancelled"
|
||||
],
|
||||
"interactions": [
|
||||
"Open/close real rows and detail disclosures; hover disk bars on desktop; use phone touch controls",
|
||||
"Select By date and Coverage using real router links; query filter to empty and back; scope to snapshot-only location and remove scope",
|
||||
"Retry History and action/facet reads; select target and range; retain denial during pending refresh; read fresh successful result",
|
||||
"Keyboard activation, Tab, Enter, arrow keys, Home, End and Escape; cancel restore before mutation and observe synthetic rejection/pending acceptance",
|
||||
"Inspect actual desktop and phone screenshots and assert rendered bounds and final data, not just route or HTTP success"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1365,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"verified_at": "2026-10-03T16:56:27Z",
|
||||
"evidence": [
|
||||
"Current invocation OUTCOME/RECEIPTS.md and hash-verified browser artifacts (nine production-component fixture families), synthetic production-component/CSS observations only"
|
||||
],
|
||||
"limitations": [
|
||||
"Not native appliance/QGA/thaw, full-app/server, installation, signed persistent-state recovery or released acceptance.",
|
||||
"Fresh additive receipt covers the 26 missing final contents against published main. Existing valid own-parent proofs are retained rather than rewritten."
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user