mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 07:06:45 +03:00
Paul's design, three pieces: serve-question.mjs: the world decision presented as a themed page instead of a text prompt. The script serves an impeccable-styled option board (assigned direction leading with THE ROLL badge, dealt challengers as alternates carrying their QUALITY BAR cards, re-roll and steer built in), prints the URL, opens the browser, and blocks until the user chooses; the answer lands on stdout as ANSWER JSON, so the shell call itself is the wait and no harness machinery is needed. Local images are served by the ephemeral server; nothing leaves the machine. generate-image.mjs + context.mjs IMAGE_GEN_AVAILABLE: when an OpenAI key is in the environment, context reports that image generation works even without a harness-native tool (gpt-image-2, billed to the user's key, stated before first use; Google skipped by decision). Harness-native tools always win when present. new-work.md: visualize-before-build is now the default whenever any image generation exists, not a codex.md special case; the attended presentation prefers the visual decision page and falls back to the structured question tool. Evals keep the unattended path untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
82 lines
3.3 KiB
JavaScript
82 lines
3.3 KiB
JavaScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { spawn } from 'node:child_process';
|
|
import { writeFileSync, mkdtempSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const SCRIPT = path.join(ROOT, 'skill', 'scripts', 'serve-question.mjs');
|
|
|
|
function startServer(payload, extraArgs = []) {
|
|
const dir = mkdtempSync(path.join(tmpdir(), 'serve-question-'));
|
|
const payloadPath = path.join(dir, 'q.json');
|
|
writeFileSync(payloadPath, JSON.stringify(payload));
|
|
const child = spawn(process.execPath, [SCRIPT, '--payload', payloadPath, '--no-open', '--timeout', '30', ...extraArgs], {
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
});
|
|
return new Promise((resolve, reject) => {
|
|
let out = '';
|
|
const timer = setTimeout(() => reject(new Error(`no URL in output: ${out}`)), 10000);
|
|
child.stdout.on('data', (chunk) => {
|
|
out += chunk;
|
|
const match = out.match(/QUESTION URL: (http:\/\/127\.0\.0\.1:\d+\/)/);
|
|
if (match) { clearTimeout(timer); resolve({ child, url: match[1], read: () => out }); }
|
|
});
|
|
});
|
|
}
|
|
|
|
const PAYLOAD = {
|
|
title: 'Choose the visual world',
|
|
question: 'The roll assigned Fillmore Handbill.',
|
|
options: [
|
|
{ id: 'assigned', label: 'Fillmore Handbill', kicker: 'THE ROLL', lineage: '1966-71 psychedelic handbills' },
|
|
{ id: 'challenger-1', label: 'Teletext Service', body: 'block-mosaic broadcast pages' },
|
|
],
|
|
reroll: true,
|
|
steer: true,
|
|
};
|
|
|
|
describe('serve-question', () => {
|
|
it('serves the page, records the answer, prints ANSWER, exits 0', async () => {
|
|
const { child, url, read } = await startServer(PAYLOAD);
|
|
const html = await (await fetch(url)).text();
|
|
assert.match(html, /Fillmore Handbill/);
|
|
assert.match(html, /THE ROLL/);
|
|
assert.match(html, /Re-roll/);
|
|
const post = await fetch(`${url}answer`, {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ optionId: 'assigned', steer: 'warmer palette' }),
|
|
});
|
|
assert.equal(post.status, 200);
|
|
const code = await new Promise((resolve) => child.on('exit', resolve));
|
|
assert.equal(code, 0);
|
|
assert.match(read(), /ANSWER: \{"optionId":"assigned","steer":"warmer palette"\}/);
|
|
});
|
|
|
|
it('re-roll answers round-trip with their own id', async () => {
|
|
const { child, url, read } = await startServer(PAYLOAD);
|
|
await fetch(`${url}answer`, {
|
|
method: 'POST',
|
|
headers: { 'content-type': 'application/json' },
|
|
body: JSON.stringify({ optionId: 'reroll', steer: '' }),
|
|
});
|
|
const code = await new Promise((resolve) => child.on('exit', resolve));
|
|
assert.equal(code, 0);
|
|
assert.match(read(), /"optionId":"reroll"/);
|
|
});
|
|
|
|
it('rejects an empty payload', async () => {
|
|
const dir = mkdtempSync(path.join(tmpdir(), 'serve-question-'));
|
|
const payloadPath = path.join(dir, 'q.json');
|
|
writeFileSync(payloadPath, JSON.stringify({ options: [] }));
|
|
const code = await new Promise((resolve) => {
|
|
const child = spawn(process.execPath, [SCRIPT, '--payload', payloadPath, '--no-open'], { stdio: 'ignore' });
|
|
child.on('exit', resolve);
|
|
});
|
|
assert.equal(code, 1);
|
|
});
|
|
});
|