mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 18:47:02 +03:00
Port /impeccable generate to the engine crates
The Node-era server, CLI, hook, and pin halves of the generate command move into the Rust workspace, with the protocol unchanged: - crates/live: POST /agent-target is held open on a channel plus a timer thread (the manual-apply deferred pattern), releasing its turnstile ticket before it parks like /poll; /agent-target-result resolves it; /agent-target-claim is the roll call with its renewable lease. SSE connections carry the overlay's clientId: a late overlay is replayed every pending target, and a disconnect retires that overlay's report, releases its lease, and re-judges each roll call. Shutdown drains held requests with server_stopping. - crates/live/src/live_generate.rs: the live-generate verb (the router already forwards every live* verb), same flags, verdicts, and _instructions, spelled with the engine's self command. - crates/hook: every entry stands down on live preview markers (skipped: live-preview), checking the proposed content and the file on disk for hook-before-edit. - crates/context: pin accepts generate; the crate's command-metadata.json copy carries its entry. Tests: crates/cli/tests/agent_target.rs (six HTTP cases with an SSE reader), tests/live-agent-target.test.mjs rewritten to drive the binary (28 cases, registered in the live suite), hook stand-down cases, oracle goldens for live-generate plus the re-recorded pin list goldens, the e2e prompt assertion waiting for the journaled event, and the contract documented in docs/CLI-CONTRACT.md. AI-assisted: implemented and tested with Claude Code under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Abdul Wahab
co-authored by
Claude Fable 5
parent
fc89b0ed62
commit
d397140a77
@@ -3,7 +3,6 @@ import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { compileProviderBlocks } from '../scripts/lib/utils.js';
|
||||
import { VISUAL_ACTIONS } from '../skill/scripts/live/vocabulary.mjs';
|
||||
|
||||
const ROOT = process.cwd();
|
||||
|
||||
@@ -178,8 +177,20 @@ describe('live reference authoring contract', () => {
|
||||
// value the picker offers but the reference never names is a request
|
||||
// the agent cannot route.
|
||||
const generateMd = readFileSync(join(ROOT, 'skill/reference/generate.md'), 'utf-8');
|
||||
for (const action of VISUAL_ACTIONS) {
|
||||
for (const action of readVisualActions()) {
|
||||
assert.match(generateMd, new RegExp('`' + action + '`'), `generate.md must name \`${action}\``);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// The action vocabulary lives in the engine (crates/live/src/vocabulary.rs);
|
||||
// read it from the Rust source so the parity check needs no binary and can
|
||||
// never drift from what the live server accepts.
|
||||
function readVisualActions() {
|
||||
const rust = readFileSync(join(ROOT, 'crates/live/src/vocabulary.rs'), 'utf-8');
|
||||
const block = rust.match(/pub const VISUAL_ACTIONS: \[&str; (\d+)\] = \[([\s\S]*?)\];/);
|
||||
if (!block) throw new Error('VISUAL_ACTIONS not found in crates/live/src/vocabulary.rs');
|
||||
const actions = [...block[2].matchAll(/"([a-z]+)"/g)].map((m) => m[1]);
|
||||
if (actions.length !== Number(block[1])) throw new Error('VISUAL_ACTIONS length mismatch');
|
||||
return actions;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user