Release: CLI 4.0.2 and engine 0.1.1

Ship signed skill-bundle verification, fix annotated-session checkpoint ordering, and pin all published engine platform packages. Validated with Rust, Node, browser, and provider-backed end-to-end tests. AI assistance: prepared and validated with Codex under Paul Bakaus direction.
This commit is contained in:
Paul Bakaus
2026-09-05 10:47:32 -07:00
committed by GitHub
parent 8dac6ae7e0
commit eebfb7c2ce
13 changed files with 143 additions and 53 deletions
Generated
+16 -16
View File
@@ -509,7 +509,7 @@ dependencies = [
[[package]]
name = "impeccable"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"base64",
"impeccable-browser",
@@ -528,7 +528,7 @@ dependencies = [
[[package]]
name = "impeccable-browser"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"base64",
"impeccable-core",
@@ -542,7 +542,7 @@ dependencies = [
[[package]]
name = "impeccable-bundle"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"base64",
"impeccable-core",
@@ -551,14 +551,14 @@ dependencies = [
[[package]]
name = "impeccable-common"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"libc",
]
[[package]]
name = "impeccable-comp"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"image",
"once_cell",
@@ -570,7 +570,7 @@ dependencies = [
[[package]]
name = "impeccable-comp-verbs"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-common",
"impeccable-comp",
@@ -583,7 +583,7 @@ dependencies = [
[[package]]
name = "impeccable-context"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"flate2",
"impeccable-common",
@@ -600,7 +600,7 @@ dependencies = [
[[package]]
name = "impeccable-core"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-core",
"impeccable-foundation",
@@ -612,7 +612,7 @@ dependencies = [
[[package]]
name = "impeccable-detect"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-common",
"impeccable-core",
@@ -624,7 +624,7 @@ dependencies = [
[[package]]
name = "impeccable-foundation"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"cssparser",
"once_cell",
@@ -637,7 +637,7 @@ dependencies = [
[[package]]
name = "impeccable-hook"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-common",
"impeccable-context",
@@ -651,7 +651,7 @@ dependencies = [
[[package]]
name = "impeccable-html"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"cssparser",
"ego-tree",
@@ -672,7 +672,7 @@ dependencies = [
[[package]]
name = "impeccable-live"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"getrandom 0.2.17",
"impeccable-common",
@@ -690,7 +690,7 @@ dependencies = [
[[package]]
name = "impeccable-skills"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-common",
"impeccable-context",
@@ -709,7 +709,7 @@ dependencies = [
[[package]]
name = "impeccable-wasm"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-core",
"impeccable-detect",
@@ -1681,7 +1681,7 @@ checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]]
name = "xtask"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"impeccable-bundle",
]
+1 -1
View File
@@ -6,7 +6,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0"
version = "0.1.1"
edition = "2021"
license = "Apache-2.0"
publish = false
+1 -1
View File
@@ -1 +1 @@
0.1.0
0.1.1
+15 -5
View File
@@ -19,11 +19,11 @@
"zod": "^4.3.6",
},
"optionalDependencies": {
"@impeccable/cli-darwin-arm64": "0.1.0",
"@impeccable/cli-darwin-x64": "0.1.0",
"@impeccable/cli-linux-arm64": "0.1.0",
"@impeccable/cli-linux-x64": "0.1.0",
"@impeccable/cli-windows-x64": "0.1.0",
"@impeccable/cli-darwin-arm64": "0.1.1",
"@impeccable/cli-darwin-x64": "0.1.1",
"@impeccable/cli-linux-arm64": "0.1.1",
"@impeccable/cli-linux-x64": "0.1.1",
"@impeccable/cli-windows-x64": "0.1.1",
},
},
},
@@ -72,6 +72,16 @@
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
"@impeccable/cli-darwin-arm64": ["@impeccable/cli-darwin-arm64@0.1.1", "", { "os": "darwin", "cpu": "arm64", "bin": { "impeccable-darwin-arm64": "bin/impeccable" } }, "sha512-1/DZYaiZqDoNwpXpyoG4gRQpgLZ4YDCMADGzA5wYmNiG279b3KPt2RLYUww6NF2hxoMxdVQUAGzdc5062KZKHg=="],
"@impeccable/cli-darwin-x64": ["@impeccable/cli-darwin-x64@0.1.1", "", { "os": "darwin", "cpu": "x64", "bin": { "impeccable-darwin-x64": "bin/impeccable" } }, "sha512-/itjFZEHPcz1RQDBx3+2aeTebQ4pCD30VKAJ3Zst5KxHteJQOW3hoiTFE7jAOXg3vG2D/qAaEh/7Eo+xzEawew=="],
"@impeccable/cli-linux-arm64": ["@impeccable/cli-linux-arm64@0.1.1", "", { "os": "linux", "cpu": "arm64", "bin": { "impeccable-linux-arm64": "bin/impeccable" } }, "sha512-uGJ2DNVq3NzH8+RlTlyn1XWpAsNS1bv6kix7PsAnzCa0aBiOHaYSUkPXbqDVUTDy9X4oEOsZYuzbgoY+otkoMQ=="],
"@impeccable/cli-linux-x64": ["@impeccable/cli-linux-x64@0.1.1", "", { "os": "linux", "cpu": "x64", "bin": { "impeccable-linux-x64": "bin/impeccable" } }, "sha512-wPul+V7w9g0MZAgFmJJvEOXpy8AYt4htmLQJIx7XvakSp8CS+PYBjEFqmWSW41NlrNAaifzDKdUe6rzeRli97A=="],
"@impeccable/cli-windows-x64": ["@impeccable/cli-windows-x64@0.1.1", "", { "os": "win32", "cpu": "x64", "bin": { "impeccable-windows-x64": "bin/impeccable.exe" } }, "sha512-dqcQ8VQFschjA1iFzhKvO44UEzsbQQGHAZW3MmsxJbqo/uSDENIL8WA5l4K8Q9wqjf4Kbc/H1hqo6MjOuQw85A=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "impeccable",
"version": "4.0.1",
"version": "4.0.2",
"author": "Paul Bakaus",
"description": "Design skills, commands, and anti-pattern detection for AI coding agents",
"keywords": [
@@ -71,11 +71,11 @@
"check:engine-release": "node scripts/check-engine-release.mjs"
},
"optionalDependencies": {
"@impeccable/cli-darwin-arm64": "0.1.0",
"@impeccable/cli-darwin-x64": "0.1.0",
"@impeccable/cli-linux-x64": "0.1.0",
"@impeccable/cli-linux-arm64": "0.1.0",
"@impeccable/cli-windows-x64": "0.1.0"
"@impeccable/cli-darwin-arm64": "0.1.1",
"@impeccable/cli-darwin-x64": "0.1.1",
"@impeccable/cli-linux-x64": "0.1.1",
"@impeccable/cli-linux-arm64": "0.1.1",
"@impeccable/cli-windows-x64": "0.1.1"
},
"devDependencies": {
"@ai-sdk/anthropic": "^4.0.7",
+1 -1
View File
@@ -1 +1 @@
0.1.0
0.1.1
+6 -4
View File
@@ -7834,7 +7834,6 @@
if (editBadgeEl && editBadgeEl.style.display !== 'none') renderEditBadge('idle-disabled');
showBar('generating');
saveSession();
sendCheckpoint('generate_started');
writeScrollY(window.scrollY);
if (variantObserver) variantObserver.disconnect();
variantObserver = startVariantObserver(currentSessionId);
@@ -7916,7 +7915,6 @@
showBar('generating');
startScrollTracking();
saveSession();
sendCheckpoint('generate_started');
writeScrollY(window.scrollY);
if (variantObserver) variantObserver.disconnect();
variantObserver = startVariantObserver(currentSessionId);
@@ -8238,7 +8236,8 @@
// rasterization from delaying the fetch itself.
if (!hasAnnotations) {
basePayload.clientSentAt = Date.now();
await sendEvent(basePayload);
const created = await sendEvent(basePayload);
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
}
let screenshotPath;
@@ -8279,7 +8278,10 @@
// is semantic input. Plain requests were already dispatched above.
if (hasAnnotations) {
basePayload.clientSentAt = Date.now();
sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
const created = await sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload);
// Capture/upload can take seconds. Progress before this acknowledgment
// refers to an unknown session and would clear our own active work.
if (created?.ok && currentSessionId === basePayload.id) sendCheckpoint('generate_started');
}
}
@@ -57,6 +57,12 @@
"readyPattern": "Local:\\s+https?://[^:]+:(\\d+)",
"readyTimeoutMs": 120000,
"steer": false,
"liveChrome": {
"annotations": {
"selector": "h1.hero-title",
"uploadDelayMs": 300
}
},
"pickSelector": "ul.expense-list",
"pickPosition": {
"x": 10,
+42 -1
View File
@@ -2,12 +2,53 @@ import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { runInNewContext } from 'node:vm';
const SOURCE = readFileSync(join(process.cwd(), 'skill/scripts/live-browser.js'), 'utf-8');
const PENDING_DOCK_POSITION_SOURCE = SOURCE.match(/function positionPendingDock\(\) \{[\s\S]*?\n \}/)?.[0] || '';
const CAPTURE_AND_EMIT_SOURCE = SOURCE.match(/async function captureAndEmit\([\s\S]*?\n \}/)?.[0] || '';
describe('live-browser source contracts', () => {
it('does not checkpoint a generation before captureAndEmit creates its session', () => {
for (const name of ['handleGo', 'handleInsertCreate']) {
const body = SOURCE.match(new RegExp(`function ${name}\\(\\) \\{[\\s\\S]*?\\n \\}`))?.[0];
assert.ok(body);
assert.doesNotMatch(body, /sendCheckpoint\('generate_started'\)/);
}
});
for (const annotated of [false, true]) {
for (const outcome of ['created', 'failed', 'superseded']) {
it(`${annotated ? 'annotated' : 'plain'} generation checkpoints only its acknowledged current session (${outcome})`, async () => {
const capture = Promise.withResolvers();
const creation = Promise.withResolvers();
const events = [];
const context = {
currentSessionId: 'session-a', state: 'GENERATING', PORT: 1234, TOKEN: 'test',
console, Date,
captureElementToBlob: () => capture.promise,
showShaderOverlay() {},
fetch: async () => ({ ok: true, json: async () => ({ path: '/annotation.png' }) }),
sendEvent: async (payload) => { events.push(payload.type); return creation.promise; },
sendCheckpoint: (reason) => events.push(reason),
};
const emit = runInNewContext(`(${CAPTURE_AND_EMIT_SOURCE})`, context);
const pending = emit({}, { type: 'generate', id: 'session-a' }, {
comments: annotated ? [{ text: 'change title' }] : [], strokes: [],
}, {});
await new Promise(resolve => setImmediate(resolve));
assert.deepEqual(events, annotated ? [] : ['generate']);
capture.resolve({ blob: {}, paper: 'white' });
await new Promise(resolve => setImmediate(resolve));
assert.deepEqual(events, ['generate'], 'capture/upload must not checkpoint before creation is acknowledged');
if (outcome === 'superseded') context.currentSessionId = 'session-b';
creation.resolve(outcome === 'failed' ? null : { ok: true });
await pending;
assert.deepEqual(events, outcome === 'created' ? ['generate', 'generate_started'] : ['generate']);
});
}
}
it('reports foreground poll connectivity without a background worker dependency', () => {
assert.match(
SOURCE,
@@ -29,7 +70,7 @@ describe('live-browser source contracts', () => {
);
assert.match(
CAPTURE_AND_EMIT_SOURCE,
/if \(hasAnnotations\) \{[\s\S]*?basePayload\.clientSentAt = Date\.now\(\);\s*sendEvent\(screenshotPath \? \{ \.\.\.basePayload, screenshotPath \} : basePayload\);\s*\}/,
/if \(hasAnnotations\) \{[\s\S]*?basePayload\.clientSentAt = Date\.now\(\);\s*const created = await sendEvent\(screenshotPath \? \{ \.\.\.basePayload, screenshotPath \} : basePayload\);/,
'annotated generation should dispatch exactly after capture and upload resolve',
);
});
+14
View File
@@ -7,6 +7,7 @@ import {
MANUAL_EDIT_SYSTEM_INSTRUCTIONS,
VARIANT_SYSTEM_INSTRUCTIONS,
createLlmAgent,
llmRequestSettings,
parseManualEditResponse,
parseVariantResponse,
progressiveVariantGuidance,
@@ -19,6 +20,19 @@ import {
validateVariantVisibleCopy,
} from './live-e2e/agents/llm-agent.mjs';
describe('live-e2e LLM request settings', () => {
it('explicitly selects low-effort DeepSeek thinking for bounded JSON edit requests', () => {
assert.deepEqual(llmRequestSettings('deepseek'), {
thinking: { type: 'enabled' }, output_config: { effort: 'low' },
});
});
it('leaves other providers unchanged', () => {
assert.deepEqual(llmRequestSettings('anthropic'), {});
assert.deepEqual(llmRequestSettings('openai'), {});
});
});
describe('live-e2e LLM agent provider config', () => {
it('defaults to OpenAI gpt-5.6-terra at medium reasoning effort', () => {
const config = resolveLlmAgentConfig({}, {});
+10 -6
View File
@@ -1283,17 +1283,21 @@ for (const { name, fixture } of fixtures) {
const pickSelector = annotation.selector || fixture.runtime.pickSelector || 'h1.hero-title';
try {
await waitForHandshake(page);
if (annotation.uploadDelayMs) {
await page.route('**/annotation?*', async (route) => {
await new Promise(resolve => setTimeout(resolve, annotation.uploadDelayMs));
await route.continue();
});
}
if (fixture.runtime.preActions) await runPreActions(page, fixture.runtime.preActions);
await pickElement(page, pickSelector, { resetPickMode: true });
await drawAnnotationPinAndStroke(page, {
comment: annotation.comment || 'Make this selected element easier to scan',
});
await clickGo(page);
await waitForCyclingRobust(page, 3, {
agentMode,
preActions: fixture.runtime.preActions,
log: (m) => t.diagnostic(m),
});
// A reload would mask a checkpoint-before-creation race by adopting
// the session again. Annotated generation must complete in this tab.
await waitForCycling(page, 3, { timeout: agentMode === 'llm' ? 180_000 : 30_000 });
const generateEvent = recordedGenerateEvents.at(-1);
await assertAnnotationUploadEvent(generateEvent);
@@ -1302,7 +1306,7 @@ for (const { name, fixture } of fixtures) {
const sourceFile = await locateSessionFile(session.appRoot);
const svelteComponentTarget = svelteComponentTargetFor(sourceFile);
await clickNext(page);
await cycleToVariant(page, 2, 3);
assert.equal(await getVisibleVariant(page), 2, 'variant 2 visible after annotated generate');
await clickAccept(page, { expectedVariant: 2 });
await waitForBarHidden(page);
+18 -1
View File
@@ -248,6 +248,17 @@ function resolveProvider(opts, env) {
return 'openai';
}
export function llmRequestSettings(provider) {
// DeepSeek defaults to high-effort thinking, which can consume the entire
// bounded response before emitting the JSON these edit tests exercise.
// Low effort retains planning for the full live spec without inheriting
// the provider's high-effort default.
// https://api-docs.deepseek.com/guides/thinking_mode/
return provider === 'deepseek'
? { thinking: { type: 'enabled' }, output_config: { effort: 'low' } }
: {};
}
/**
* Anthropic-SDK-shaped shim over the `ai` SDK for OpenAI models, so the
* three text-only call sites in this file stay provider-agnostic. system
@@ -331,6 +342,7 @@ export async function createLlmAgent(opts = {}) {
try {
response = await client.messages.create(
{
...llmRequestSettings(provider),
model,
temperature: 0,
max_tokens: 16000,
@@ -476,6 +488,7 @@ export async function createLlmAgent(opts = {}) {
try {
response = await client.messages.create(
{
...llmRequestSettings(provider),
model,
temperature: 0,
max_tokens: 16000,
@@ -605,11 +618,15 @@ export async function createLlmAgent(opts = {}) {
].join('\n');
const response = await client.messages.create({
...llmRequestSettings(provider),
model,
max_tokens: 4096,
system: systemBlocks(STEER_SYSTEM_INSTRUCTIONS),
messages: [{ role: 'user', content: userMessage }],
});
}, provider === 'deepseek' ? {
maxRetries: LLM_REQUEST_MAX_RETRIES,
timeout: MANUAL_EDIT_REQUEST_TIMEOUT_MS,
} : {});
const cacheRead = response.usage?.cache_read_input_tokens ?? 0;
const inputTokens = response.usage?.input_tokens ?? 0;
+7 -11
View File
@@ -26,8 +26,7 @@ import {
clickEditCopy,
clickExitLiveMode,
clickGo,
clickNext,
clickPrev,
cycleToVariant,
clickSaveEdit,
drawAnnotationPinAndStroke,
editTextLeaf,
@@ -40,6 +39,7 @@ import {
waitForBarHidden,
waitForCycling,
waitForHandshake,
waitForVariantSettled,
} from './live-e2e/ui.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -281,7 +281,7 @@ async function runAnnotationGenerateFlow({ page, tmp, evidence }) {
const generateEvent = latestJournalEvent(tmp, (event) => event.type === 'generate' && event.screenshotPath);
await assertAnnotationUploadEvent(generateEvent);
assert.ok(existsSync(generateEvent.screenshotPath), 'annotation screenshot file exists');
await clickNext(page);
await cycleTo(page, 2);
await assertVariantCounter(page, 2, 3);
await evidence.capture('annotation-cycle');
await clickDiscard(page);
@@ -514,14 +514,10 @@ async function clickPendingTrash(page) {
}
async function cycleTo(page, target) {
for (let i = 0; i < 6; i++) {
const visible = await getVisibleVariant(page);
if (visible === target) return;
if (visible == null) await page.waitForTimeout(250);
else if (visible < target) await clickNext(page);
else await clickPrev(page);
}
assert.equal(await getVisibleVariant(page), target, `variant ${target} visible`);
// Component imports finish after the counter changes. Do not send the next
// click (or reload) while the previous variant is still mounting.
await cycleToVariant(page, target, 3);
await waitForVariantSettled(page, target, 3);
}
async function waitForVisibleCycling(page, count, { timeout }) {