Files
XZBT/tools/build-scenario-acceptance.mjs
LabyricornandClaude Opus 5 1cde2f9f68 feat(scenario): implement the phase 6 scenario director
Add Format Specification section 21 (Scenario Model 0.1) and the runtime
that realizes it: the scenario director, seven trigger classes, timelines
with repeats and branches, admission and concurrency control, nested
ownership with bounded cleanup, and the production GC5 resource counters.

Exhibit E provides a reproducible forty-minute long-scenario reference and
scenario-challenge.xzbt covers the PRD 131 cases. A standalone
acceptance/soak page is built by tools/build-scenario-acceptance.mjs.

252 automated checks pass. The two-hour real-duration development soak
required by the GC6 schedule remains pending.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01M7dgfQ12mpM4JjSMv3inLA
2026-09-06 22:54:31 +00:00

45 lines
4.1 KiB
JavaScript

import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { bundleRuntime } from './build-xzbt.mjs';
import { longScenarioExhibit, scenarioChallenge } from './scenario-fixtures.mjs';
import { validateExhibit } from '../src/runtime/validator.js';
export function buildScenarioAcceptance() {
const root = fileURLToPath(new URL('..', import.meta.url));
const workload = structuredClone(longScenarioExhibit);
workload.meta = { id: 'phase6-soak', name: 'Phase 6 development soak', version: '1.0.0' };
workload.runtime.seed = 4206;
workload.scenarios.pulse = { trigger: { type: 'interval', every: '20s' }, duration: '5s', onStart: [
{ type: 'event', event: 'signal' }, { type: 'sound', sound: 'drone' },
{ type: 'override', target: 'parameters.activity', value: 0.9, scope: 'scenario', transition: { out: '2s' } }
], timeline: [] };
const fixtures = [longScenarioExhibit, scenarioChallenge, workload];
mkdirSync(resolve(root, 'prototypes/phase6'), { recursive: true });
for (const document of fixtures) {
const result = validateExhibit(document);
if (!result.valid) throw new Error(JSON.stringify(result.errors));
writeFileSync(resolve(root, document === workload ? 'prototypes/phase6/workload-v1.xzbt' : `exhibits/${document.meta.id}.xzbt`), JSON.stringify(document, null, 2) + '\n');
}
const runtime = bundleRuntime(false);
const data = JSON.stringify(fixtures).replace(/</g, '\\u003c');
const identity = JSON.stringify({ workloadSha256: createHash('sha256').update(JSON.stringify(workload)).digest('hex'), runtimeSha256: createHash('sha256').update(runtime).digest('hex') });
const script = readFileSync(resolve(root, 'prototypes/phase6/acceptance.js'), 'utf8');
const html = `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>XZBT Scenario Acceptance</title><style>
body{margin:0;background:#101d25;color:#e4efed;font:15px system-ui}main{max-width:1200px;margin:auto;padding:24px}h1{font-size:24px}button,select,textarea{font:inherit;background:#203b45;color:inherit;border:1px solid #6c9999;border-radius:5px;padding:9px;margin:4px}button:disabled{opacity:.45}canvas{width:100%;height:52vh;display:block;background:#101d25}#scenarios{display:flex;flex-wrap:wrap;gap:12px}article{border:1px solid #48636a;padding:12px}output,pre{font:13px ui-monospace;white-space:pre-wrap}textarea{display:block;width:95%;min-height:55px}label{display:block;margin-top:10px}.hint{color:#b5cbc7}
</style><main><h1>Scenario acceptance / XZBT 0.1</h1>
<p class="hint">Start or cancel a scenario, inspect its status, or run the frozen two-hour development workload. Acceleration verifies logical behavior; the soak requires real elapsed time and audio output.</p>
<select id="fixture" aria-label="Exhibit"></select><button id="restart">Restart</button><button id="audio">Start audio</button><button id="pause">Pause</button><button id="accelerate">Advance 40 logical minutes</button><button id="soak">Start two-hour soak</button><button id="export">Export evidence</button>
<canvas id="canvas" aria-label="Scenario visuals"></canvas><div id="scenarios"></div>
<button id="event">Invoke request event</button><button id="condition">Set condition true</button>
<p><output id="status" aria-live="polite"></output></p><pre id="counters"></pre>
<label>Reference computer: CPU, GPU/driver, RAM, OS, power mode<textarea id="environment"></textarea></label>
<label>Listening and display observations, including any clicks, clipping or glitches<textarea id="observations"></textarea></label>
<pre id="diagnostics"></pre></main><script>'use strict'; (() => {${runtime}\nconst scenarioFixtures = ${data}; const scenarioBuild = ${identity};\n${script}\n})();</script></html>`;
const path = resolve(root, 'prototypes/phase6/XZBT-scenario-acceptance.html');
writeFileSync(path, html); return { path, html, fixtures };
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) console.log(buildScenarioAcceptance().path);