Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions evals/deck-review-report.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { buildReport } from '../plugins/visual-explainer/scripts/verify/lib/report.mjs';

function context(overrides = {}) {
return {
filePath: '/tmp/artifact.html',
profile: 'page',
preset: 'custom',
presetHint: 'custom',
html: '<main>ordinary page</main>',
...overrides,
};
}

test('routes fixed-stage presentations through the dedicated deck review pass', () => {
const report = buildReport(context({
html: '<div data-ve-presentation="true"></div>',
}), []);
assert.ok(report.llm_passes_required.includes('deck-review'));
assert.equal(
report.llm_dispatch_plan.find((entry) => entry.pass === 'deck-review')?.reason,
'no-eval-qualified-model',
);
});

test('routes slide decks but leaves magazines and ordinary pages alone', () => {
const slides = buildReport(context({ profile: 'slides' }), []);
assert.ok(slides.llm_passes_required.includes('deck-review'));

const magazine = buildReport(context({ profile: 'magazine' }), []);
assert.equal(magazine.llm_passes_required.includes('deck-review'), false);

const page = buildReport(context(), []);
assert.equal(page.llm_passes_required.includes('deck-review'), false);
});
164 changes: 164 additions & 0 deletions evals/deck-review-set.browser.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
import { runBrowserStage } from '../plugins/visual-explainer/scripts/verify/lib/browser.mjs';

const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const FIXTURE = path.join(ROOT, 'evals/fixtures/deck-review/presentation-states.html');

test('browser stage captures every presentation base, drill, and progressive state', async () => {
const screensDir = fs.mkdtempSync(path.join(os.tmpdir(), 'artifacture-deck-review-'));
const html = fs.readFileSync(FIXTURE, 'utf8');
const ctx = {
filePath: FIXTURE,
html,
profile: 'page',
preset: 'custom',
flags: { hasAnimations: false, hasMermaid: false },
};

try {
const browser = await runBrowserStage(ctx, { screensDir, profile: 'page' });
assert.equal(browser.runs.length, 4, 'the frozen page browser matrix remains unchanged');
const reviewSets = fs.readdirSync(screensDir)
.filter((name) => /^deck-review-.*\.json$/.test(name))
.sort()
.map((name) => JSON.parse(fs.readFileSync(path.join(screensDir, name), 'utf8')));
assert.equal(reviewSets.length, 2, 'light and dark desktop runs receive sidecar review sets');
for (const reviewSet of reviewSets) {
assert.deepEqual(
reviewSet.units.map((unit) => unit.state_id),
[
'slide-one--base',
'slide-one--drill--evidence',
'slide-two--base',
'slide-two--state--1',
'slide-two--drill--state-1-progressive-evidence',
],
);
assert.ok(reviewSet.units.every((unit) => fs.existsSync(unit.screenshot_path)));
assert.deepEqual(reviewSet.viewport, { width: 1440, height: 900 });
assert.deepEqual(
reviewSet.review_groups.map((group) => [group.purpose, group.state_ids]),
[
['state-continuity', ['slide-one--base', 'slide-one--drill--evidence']],
['state-continuity', ['slide-two--base', 'slide-two--state--1']],
['state-continuity', ['slide-two--base', 'slide-two--drill--state-1-progressive-evidence']],
['adjacent-slide-variety', ['slide-one--base', 'slide-two--base']],
],
);
}
} finally {
fs.rmSync(screensDir, { recursive: true, force: true });
}
});

test('bounded recapture fails when any requested state id is stale or missing', async () => {
const screensDir = fs.mkdtempSync(path.join(os.tmpdir(), 'artifacture-deck-review-missing-'));
const html = fs.readFileSync(FIXTURE, 'utf8');
const priorFilter = process.env.ARTIFACTURE_DECK_REVIEW_STATES;
process.env.ARTIFACTURE_DECK_REVIEW_STATES = 'slide-two--base,missing--state';
const ctx = {
filePath: FIXTURE,
html,
profile: 'page',
preset: 'custom',
flags: { hasAnimations: false, hasMermaid: false },
};

try {
await assert.rejects(
runBrowserStage(ctx, { screensDir, profile: 'page' }),
/did not capture requested states: missing--state/,
);
} finally {
if (priorFilter === undefined) delete process.env.ARTIFACTURE_DECK_REVIEW_STATES;
else process.env.ARTIFACTURE_DECK_REVIEW_STATES = priorFilter;
fs.rmSync(screensDir, { recursive: true, force: true });
}
});

test('bounded recapture fails closed when a requested state has no paired context', async () => {
const screensDir = fs.mkdtempSync(path.join(os.tmpdir(), 'artifacture-deck-review-unpaired-'));
const html = fs.readFileSync(FIXTURE, 'utf8');
const priorFilter = process.env.ARTIFACTURE_DECK_REVIEW_STATES;
process.env.ARTIFACTURE_DECK_REVIEW_STATES = 'slide-two--base';
const ctx = {
filePath: FIXTURE,
html,
profile: 'page',
preset: 'custom',
flags: { hasAnimations: false, hasMermaid: false },
};

try {
await assert.rejects(
runBrowserStage(ctx, { screensDir, profile: 'page' }),
/requires paired evidence for states: slide-two--base/,
);
} finally {
if (priorFilter === undefined) delete process.env.ARTIFACTURE_DECK_REVIEW_STATES;
else process.env.ARTIFACTURE_DECK_REVIEW_STATES = priorFilter;
fs.rmSync(screensDir, { recursive: true, force: true });
}
});

test('bounded recapture rejects a drill or progressive state without its base', async () => {
const screensDir = fs.mkdtempSync(path.join(os.tmpdir(), 'artifacture-deck-review-orphan-'));
const html = fs.readFileSync(FIXTURE, 'utf8');
const priorFilter = process.env.ARTIFACTURE_DECK_REVIEW_STATES;
process.env.ARTIFACTURE_DECK_REVIEW_STATES = 'slide-two--drill--state-1-progressive-evidence';
const ctx = {
filePath: FIXTURE,
html,
profile: 'page',
preset: 'custom',
flags: { hasAnimations: false, hasMermaid: false },
};

try {
await assert.rejects(
runBrowserStage(ctx, { screensDir, profile: 'page' }),
/requires two distinct states in every group/,
);
} finally {
if (priorFilter === undefined) delete process.env.ARTIFACTURE_DECK_REVIEW_STATES;
else process.env.ARTIFACTURE_DECK_REVIEW_STATES = priorFilter;
fs.rmSync(screensDir, { recursive: true, force: true });
}
});

test('browser stage can recapture one exact affected state with its base context', async () => {
const screensDir = fs.mkdtempSync(path.join(os.tmpdir(), 'artifacture-deck-review-filter-'));
const html = fs.readFileSync(FIXTURE, 'utf8');
const priorFilter = process.env.ARTIFACTURE_DECK_REVIEW_STATES;
process.env.ARTIFACTURE_DECK_REVIEW_STATES = 'slide-two--base,slide-two--drill--state-1-progressive-evidence';
const ctx = {
filePath: FIXTURE,
html,
profile: 'page',
preset: 'custom',
flags: { hasAnimations: false, hasMermaid: false },
};

try {
await runBrowserStage(ctx, { screensDir, profile: 'page' });
const manifests = fs.readdirSync(screensDir)
.filter((name) => /^deck-review-.*\.json$/.test(name))
.map((name) => JSON.parse(fs.readFileSync(path.join(screensDir, name), 'utf8')));
assert.equal(manifests.length, 2);
assert.ok(manifests.every((manifest) =>
manifest.units.length === 2
&& manifest.units[0].state_id === 'slide-two--base'
&& manifest.units[1].state_id === 'slide-two--drill--state-1-progressive-evidence'
&& manifest.review_groups.length === 1
&& manifest.review_groups[0].state_ids.length === 2));
} finally {
if (priorFilter === undefined) delete process.env.ARTIFACTURE_DECK_REVIEW_STATES;
else process.env.ARTIFACTURE_DECK_REVIEW_STATES = priorFilter;
fs.rmSync(screensDir, { recursive: true, force: true });
}
});
62 changes: 62 additions & 0 deletions evals/fixtures/deck-review/presentation-states.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { box-sizing: border-box; }
html, body, #deck { width: 100%; height: 100%; margin: 0; overflow: hidden; }
body { background: #171714; color: #f8f4ea; font: 22px/1.4 system-ui, sans-serif; }
main { position: absolute; inset: 0; padding: 90px; background: #25251f; }
section { width: 100%; height: 100%; }
h1 { font-size: 64px; }
button { min-width: 160px; min-height: 56px; }
[data-drill-open] { position: absolute; inset: 120px; padding: 60px; background: #f8f4ea; color: #171714; }
</style>
</head>
<body>
<div id="deck" data-ve-presentation="true">
<main data-slide-index="0" data-slide-id="slide-one">
<section>
<h1>One base and one drill</h1>
<button data-drill-target="evidence">Open evidence</button>
</section>
</main>
</div>
<script>
const deck = document.querySelector('#deck');
let index = 0;
let state = 0;

function render() {
if (index === 0) {
deck.innerHTML = '<main data-slide-index="0" data-slide-id="slide-one"><section><h1>One base and one drill</h1><button data-drill-target="evidence">Open evidence</button></section></main>';
deck.querySelector('[data-drill-target]').addEventListener('click', () => {
const dialog = document.createElement('aside');
dialog.setAttribute('data-drill-open', 'true');
dialog.innerHTML = '<h2>Evidence click-in</h2><button data-drill-close>Close</button>';
dialog.querySelector('[data-drill-close]').addEventListener('click', () => dialog.remove());
deck.querySelector('main').append(dialog);
});
} else {
deck.innerHTML = '<main data-slide-index="1" data-slide-id="slide-two"><section><h1>One progressive state</h1><div data-presentation-state-nav="true" data-presentation-state-index="' + state + '" data-presentation-state-count="2">State ' + state + '</div>' + (state === 1 ? '<button data-drill-target="progressive-evidence">Open progressive evidence</button>' : '') + '</section></main>';
const trigger = deck.querySelector('[data-drill-target]');
if (trigger) trigger.addEventListener('click', () => {
const dialog = document.createElement('aside');
dialog.setAttribute('data-drill-open', 'true');
dialog.innerHTML = '<h2>Progressive evidence click-in</h2><button data-drill-close>Close</button>';
dialog.querySelector('[data-drill-close]').addEventListener('click', () => dialog.remove());
deck.querySelector('main').append(dialog);
});
}
}

window.addEventListener('keydown', (event) => {
if (event.key === 'Home') { index = 0; state = 0; render(); }
if (event.key === 'ArrowRight' && index === 0) { index = 1; state = 0; render(); }
if (event.key === 'ArrowDown' && index === 1 && state === 0) { state = 1; render(); }
});
render();
</script>
</body>
</html>
6 changes: 4 additions & 2 deletions evals/visual-model-policy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ A cheaper larger model does not displace a qualified smaller model. Candidate

## Corpus

`corpus.json` defines a deliberately compact seed corpus: 36 paired scenarios
and 72 label-blind states across the owned families:
`corpus.json` defines a deliberately compact seed corpus: 42 paired scenarios
and 84 label-blind states across the owned families:

- layout, including clipping, focal crowding, authored versus accidental dead
space, and repeated-track symmetry;
- deck review, including rendered example truth, annotation mapping, reading
path density, adjacent-slide variety, and click-in continuity;
- diagram fidelity;
- aesthetic and preset fidelity;
- operating-model fidelity; and
Expand Down
Loading
Loading