open-standards/wai/quantum-studio/build-views.mjs
Transaction Science 2778ddd525 wai-quantum: the planner under a contract, its reasoning sealed, a provider's answer shadowed
Re-landed from PR #668 (branch wai/quantum-planner-contract, merged with main
495651ddd at 37350335b) as one commit on main, under the land-it model. Five
review rounds; round 5's findings (two high: workers that died as PID 1) were
fixed on the branch, and its CI reds were traced and fixed before landing.

A job may carry a contract (deadline, energy ceiling, cost, accuracy); the
planner chooses a route that meets it or refuses with every route's reason,
and its reasoning is sealed in the receipt. Under #676's reading child the
contract job is planned in the child from the per-method timings the parent
sends, bounded as a job naming its chosen method plus its reasoning, kept
across a restart. Both verifiers (Rust and receipt_verify.py) check the
planner statement beside wasmQ's; the receipts corpus is regenerated with
both kinds (268 crafted receipts, 14 plants caught). The views page carries
its engine raw-deflated and stays under its 1 MiB limit (669,484 bytes).

Passed before the push (Framework, linux; M3, macOS): every repository
check, receipts conformance, breaking, corpora, catalogue, the qpu clients,
energy (wai-rs, wai-quantum full, serve), the CLI tests and clippy, writer,
smoke, cross-simulator corpus, job receipts, wasmQ tests and vectors, the
engine with and without receipts under -D warnings, the docs pages, and the
service suite with clippy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 17:42:46 -05:00

132 lines
7.8 KiB
JavaScript

// Build the views an agent host draws (src/views/) into one self-contained
// page: the view's script (the Studio's components, bundled), the Studio's
// styles, and the engine inside the view (wai/wai-quantum-views) twice, as
// WebAssembly and as the same build translated to JavaScript, each raw-deflated
// and then base64: the view inflates the form it runs with the platform's
// DecompressionStream. The page
// loads nothing and connects nowhere. The servers serve it for each of the six
// views, naming the view in its <meta name="wai-view">.
//
// node build-views.mjs --wasm VIEWS.wasm --js VIEWS.mjs --out DIR
//
// writes DIR/wai-quantum-views.html and DIR/manifest.json (each part's size and
// SHA-256, and every source the page is built from with its SHA-256). Run it
// through wai/wai-quantum-views/build.sh, which builds the two engines first.
//
// node build-views.mjs --check
//
// says whether the committed page (wai/wai-quantum/views/) is built from the
// sources as they are: every source it names unchanged, and no source added.
import { build, transform } from 'esbuild';
import { createHash } from 'node:crypto';
import { deflateRawSync } from 'node:zlib';
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const repo = join(here, '..', '..');
const arg = (k) => { const i = process.argv.indexOf(k); return i > 0 ? process.argv[i + 1] : null; };
const [wasmPath, jsPath, out] = [arg('--wasm'), arg('--js'), arg('--out')];
const checking = process.argv.includes('--check');
if (!checking && (!wasmPath || !jsPath || !out)) {
console.error('usage: node build-views.mjs --wasm VIEWS.wasm --js VIEWS.mjs --out DIR');
process.exit(2);
}
// The most a view may be: the servers' tests hold every view to it.
export const LIMIT = 1 << 20;
// The name the servers replace with the view's.
export const PLACEHOLDER = '__WAI_QUANTUM_VIEW__';
const sha = (b) => createHash('sha256').update(b).digest('hex');
const bytes = (s) => Buffer.byteLength(s, 'utf8');
// The views are in English: the Studio's components they share read their
// words through src/i18n.js, and here its locales are English alone, so the
// page carries no other language's words.
const englishOnly = {
name: 'english-only',
setup(b) {
b.onResolve({ filter: /\/locales\/index\.js$/ }, () => ({ path: 'english-only', namespace: 'wai-views-locales' }));
b.onLoad({ filter: /.*/, namespace: 'wai-views-locales' }, () => {
const dir = join(here, 'locales', 'en');
const areas = readdirSync(dir).filter((f) => f.endsWith('.json')).sort();
const names = areas.map((f, i) => `en${i}`);
return {
resolveDir: dir,
loader: 'js',
contents: `${areas.map((f, i) => `import ${names[i]} from './${f}';`).join('\n')}\nexport const AREAS = ${JSON.stringify(areas.map((f) => f.replace(/\.json$/, '')))};\nexport default { en: Object.assign({}, ${names.join(', ')}) };\n`,
};
});
},
};
// The view's script, and every module it bundles.
const app = await build({ absWorkingDir: here, entryPoints: [join(here, 'src/views/main.js')], bundle: true, format: 'esm', target: ['es2022'], minify: true, legalComments: 'none', write: false, metafile: true, logLevel: 'warning', plugins: [englishOnly] });
const script = app.outputFiles[0].text;
const styleFiles = ['src/tokens.css', 'src/studio.css', 'src/views/views.css'];
// Every source, relative to the repository.
const sourcesOf = (inputs) => [
...Object.keys(inputs).filter((p) => !p.startsWith('wai-views-locales:')).map((p) => join(here, p)),
...styleFiles.map((f) => join(here, f)),
join(here, 'build-views.mjs'),
...['src/lib.rs', 'Cargo.toml', 'Cargo.lock', 'build.sh'].map((f) => join(repo, 'wai/wai-quantum-views', f)),
].map((p) => relative(repo, p).split('\\').join('/')).sort();
if (checking) {
const committed = JSON.parse(readFileSync(join(repo, 'wai/wai-quantum/views/manifest.json'), 'utf8'));
const now = sourcesOf(app.metafile.inputs);
const stale = now.filter((p) => committed.sources[p] !== sha(readFileSync(join(repo, p))));
const gone = Object.keys(committed.sources).filter((p) => !now.includes(p));
if (stale.length || gone.length) {
console.error(`the committed views page is not built from these sources: ${[...stale.map((p) => `${p} changed`), ...gone.map((p) => `${p} no longer used`)].join('; ')}. Rebuild it: bash wai/wai-quantum-views/build.sh`);
process.exit(1);
}
console.log(`the committed views page is built from these ${now.length} sources`);
process.exit(0);
}
// The engine as JavaScript, made a factory: it runs only when a host's policy refuses the WebAssembly.
const engineJs = (await transform(readFileSync(jsPath, 'utf8'), { format: 'iife', globalName: '__wqEngine', minify: true, legalComments: 'none', target: 'es2022' })).code;
const factory = `globalThis.wqEngineJs=function(){${engineJs};return __wqEngine};`;
const wasm = readFileSync(wasmPath);
// Both forms carried deflated (RFC 1951, zlib's best compression): the
// WebAssembly in less than half its bytes and the JavaScript in about a fifth,
// and the page within its limit with the engine whole, in both forms.
const wasmDeflated = deflateRawSync(wasm, { level: 9 });
const factoryDeflated = deflateRawSync(Buffer.from(factory, 'utf8'), { level: 9 });
const css = (await transform(styleFiles.map((f) => readFileSync(join(here, f), 'utf8')).join('\n'), { loader: 'css', minify: true, legalComments: 'none' })).code;
// Text inside a <script> or <style> must not end it early.
const inert = (s, tag) => s.replace(new RegExp(`</${tag}`, 'gi'), `<\\/${tag}`);
const html = `<!doctype html>
<html lang="en" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="wai-view" content="${PLACEHOLDER}">
<meta name="generator" content="wai-quantum-views">
<title>WAI Quantum</title>
<style>${inert(css, 'style')}</style>
</head>
<body>
<main id="wq" aria-live="polite"></main>
<script>globalThis.WQ_ENGINE_WASM="${wasmDeflated.toString('base64')}";globalThis.WQ_ENGINE_JS="${factoryDeflated.toString('base64')}";</script>
<script type="module">${inert(script, 'script')}</script>
</body>
</html>
`;
if (html.split(PLACEHOLDER).length !== 2) throw new Error('the page must name its view in one place');
if (bytes(html) > LIMIT) throw new Error(`the page is ${bytes(html)} bytes, past the limit of ${LIMIT}`);
const sources = sourcesOf(app.metafile.inputs);
const manifest = {
format: 'wai.quantum.views-bundle/1',
about: 'The page every view of WAI Quantum is served from, the view named in its <meta name="wai-view">. Built by wai/wai-quantum-views/build.sh; do not edit it by hand.',
placeholder: PLACEHOLDER,
limit_bytes: LIMIT,
bytes: { page: bytes(html), script: bytes(script), styles: bytes(css), engine_wasm: wasm.length, engine_wasm_deflated: wasmDeflated.length, engine_wasm_base64: Math.ceil(wasmDeflated.length / 3) * 4, engine_js: bytes(factory), engine_js_deflated: factoryDeflated.length, engine_js_base64: Math.ceil(factoryDeflated.length / 3) * 4 },
sha256: { page: sha(html), script: sha(script), styles: sha(css), engine_wasm: sha(wasm), engine_wasm_deflated: sha(wasmDeflated), engine_js: sha(factory), engine_js_deflated: sha(factoryDeflated) },
sources: Object.fromEntries(sources.map((p) => [p, sha(readFileSync(join(repo, p)))])),
};
mkdirSync(out, { recursive: true });
writeFileSync(join(out, 'wai-quantum-views.html'), html);
writeFileSync(join(out, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
console.log(`wrote ${join(out, 'wai-quantum-views.html')}: ${bytes(html)} bytes (script ${bytes(script)}, styles ${bytes(css)}, engine ${wasm.length} as WebAssembly (${wasmDeflated.length} deflated) and ${bytes(factory)} as JavaScript (${factoryDeflated.length} deflated))`);