mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
The maintainer's field run took five and a half minutes from the prompt to variants on screen. Two baseline runs on the same repo reproduced it (356 s mean): 68 KB of skill text read before the first variant (a 36 KB live.md among it), six to ten tool calls spent finding the dev URL and the selector, 9 to 10 KB of variants carrying tune knobs, and a document read plus a detect pass after the accept. generate.md is now the whole contract for the lane and never sends the agent to live.md, craft-floor.md, or the action reference on the happy path; the floors are inlined. The engine carries the rest: a generate started by live-generate is journaled and queued with origin "agent", and its poll instructions hand out the fast path (identity from the event's computed styles and custom properties, the action's three dimensions, no knobs unless asked, one edit, reply done) instead of the interactive planning pointer. `impeccable live --allow-missing-context` boots without PRODUCT.md or DESIGN.md, naming what is missing, so the lane never falls into the init interview; the boot also reports devUrl, the origin whose page carries the injected tag, so the agent opens the page instead of reading terminals. Accept is a bake and live-complete is its verification: no detect pass, no document read. Three trimmed runs (one without any context files) averaged 179 s from prompt to variants, 21 tool calls and 106k tokens against the baseline's 356 s, 30 tool calls and 144k tokens; the accept bake went from 67 s to 41 s. Method and numbers: tmp/questionaire/plan41-field-tests/SNAPPY-REPORT.md in the maintainer's checkout. Tests: dev_url probe unit tests, a fast-path instructions unit test, the origin marker in the protocol suite, and tests/live-boot-fastpath.test.mjs (flag, contextMissing, devUrl through a stand-in dev server); contract doc updated. Written with AI assistance (Claude). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
4.5 KiB
JavaScript
87 lines
4.5 KiB
JavaScript
/**
|
|
* The generate command's fast lane through the live boot: `--allow-missing-context`
|
|
* boots a project that has no PRODUCT.md / DESIGN.md (naming what is missing
|
|
* instead of refusing), and the boot reports `devUrl`, the dev server that is
|
|
* serving the injected page right now, so the agent never reads terminals.
|
|
*/
|
|
import { describe, it, before, after } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { createServer } from 'node:http';
|
|
import { execFile } from 'node:child_process';
|
|
import { ENGINE_MISSING_MESSAGE, engineEnv, findEngineBinary } from './lib/engine-bin.mjs';
|
|
|
|
const ENGINE_BIN = findEngineBinary();
|
|
|
|
// Async on purpose: the stand-in dev server below lives in this process, so
|
|
// a blocking exec would freeze the event loop while the boot probes it.
|
|
function run(cwd, args, env = {}) {
|
|
return new Promise((resolve) => {
|
|
execFile(ENGINE_BIN, args, { cwd, encoding: 'utf-8', env: engineEnv(ENGINE_BIN, env) }, (err, stdout) => {
|
|
const text = (stdout || err?.stdout || '').trim();
|
|
if (!text) return resolve({ ok: false, error: 'no_output', detail: String(err) });
|
|
// `live-server stop` answers in prose ("Stopped live server on port N.").
|
|
try { resolve(JSON.parse(text)); } catch { resolve({ ok: !err, raw: text }); }
|
|
});
|
|
});
|
|
}
|
|
|
|
describe('live boot fast lane', { skip: ENGINE_BIN ? false : ENGINE_MISSING_MESSAGE }, () => {
|
|
let tmp;
|
|
let server;
|
|
let devUrl;
|
|
before(async () => {
|
|
tmp = mkdtempSync(join(tmpdir(), 'impeccable-boot-fastlane-'));
|
|
writeFileSync(join(tmp, 'package.json'), JSON.stringify({ name: 'fastlane', scripts: { dev: 'vite' } }));
|
|
writeFileSync(join(tmp, 'vite.config.js'), 'export default {}\n');
|
|
writeFileSync(join(tmp, 'index.html'), '<!doctype html><html><body><h1 id="hero">Hero</h1></body></html>\n');
|
|
mkdirSync(join(tmp, '.impeccable/live'), { recursive: true });
|
|
writeFileSync(join(tmp, '.impeccable/live/config.json'), JSON.stringify({"files": ["index.html"], "insertBefore": "</body>", "commentSyntax": "html"}));
|
|
// A stand-in dev server: serves the project's index.html as it is on disk,
|
|
// injected tag included, the way Vite would.
|
|
server = createServer((req, res) => {
|
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
res.end(readFileSync(join(tmp, 'index.html'), 'utf-8'));
|
|
});
|
|
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
|
devUrl = `http://127.0.0.1:${server.address().port}/`;
|
|
});
|
|
after(async () => {
|
|
if (existsSync(join(tmp, '.impeccable/live/server.json'))) await run(tmp, ['live-server', 'stop']);
|
|
await new Promise((r) => server.close(r));
|
|
rmSync(tmp, { recursive: true, force: true });
|
|
});
|
|
|
|
it('refuses without the flag, boots with it, naming what is missing, and finds the dev server', async () => {
|
|
const refused = await run(tmp, ['live']);
|
|
assert.equal(refused.ok, false);
|
|
assert.equal(refused.error, 'context_missing');
|
|
assert.deepEqual(refused.missing, ['PRODUCT.md', 'DESIGN.md']);
|
|
|
|
const booted = await run(tmp, ['live', '--allow-missing-context'], { IMPECCABLE_DEV_URL_CANDIDATES: `http://127.0.0.1:1/, ${devUrl}` });
|
|
assert.equal(booted.ok, true, JSON.stringify(booted));
|
|
assert.deepEqual(booted.contextMissing, ['PRODUCT.md', 'DESIGN.md']);
|
|
assert.match(booted.contextNote, /do not run init or document/);
|
|
assert.equal(booted.hasProduct, false);
|
|
assert.equal(booted.hasDesign, false);
|
|
assert.equal(booted.devUrl, devUrl, 'the origin serving the injected page is reported');
|
|
assert.ok(readFileSync(join(tmp, 'index.html'), 'utf-8').includes('live.js?token='), 'the page was injected');
|
|
|
|
const stopped = await run(tmp, ['live-server', 'stop']);
|
|
assert.ok(stopped.ok !== false, JSON.stringify(stopped));
|
|
});
|
|
|
|
it('reports devUrl null when nothing serves the injected page, and no contextMissing when both files exist', async () => {
|
|
writeFileSync(join(tmp, 'PRODUCT.md'), '# Product\n\n## Platform\n\nweb\n');
|
|
writeFileSync(join(tmp, 'DESIGN.md'), '---\nname: Test\n---\n# Design\n');
|
|
const booted = await run(tmp, ['live'], { IMPECCABLE_DEV_URL_CANDIDATES: 'http://127.0.0.1:1/' });
|
|
assert.equal(booted.ok, true, JSON.stringify(booted));
|
|
assert.deepEqual(booted.contextMissing, []);
|
|
assert.equal(booted.contextNote, null);
|
|
assert.equal(booted.devUrl, null);
|
|
await run(tmp, ['live-server', 'stop']);
|
|
});
|
|
});
|