import { mkdirSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { bundleRuntime } from './build-xzbt.mjs'; import { visualChallenges, visualExhibits } from './visual-challenge-fixtures.mjs'; import { validateExhibit } from '../src/runtime/validator.js'; export function buildVisualAcceptance() { const root = fileURLToPath(new URL('..', import.meta.url)); const fixtures = [...visualChallenges, ...visualExhibits]; mkdirSync(resolve(root, 'exhibits/visual-challenges'), { recursive: true }); mkdirSync(resolve(root, 'prototypes/phase4'), { recursive: true }); for (const fixture of fixtures) { const result = validateExhibit(fixture.document); if (!result.valid) throw new Error(JSON.stringify(result.errors)); writeFileSync(resolve(root, fixture.challenge ? 'exhibits/visual-challenges' : 'exhibits', `${fixture.id}.xzbt`), JSON.stringify(fixture.document, null, 2) + '\n'); } const data = JSON.stringify(fixtures).replace(/ XZBT Visual Challenge

Visual Challenge / XZBT 0.1

This page supplies synthetic audio signals for reproducible visual checks. It does not establish live audio acceptance or benchmark results. Judge the composition on your display and record observations below.

`; const path = resolve(root, 'prototypes/phase4/XZBT-visual-acceptance.html'); writeFileSync(path, html); return { path, count: fixtures.length, html }; } if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { const result = buildVisualAcceptance(); console.log(`Built ${result.path}; ${result.count} fixtures.`); }