Files
pbakaus_impeccable/tests/live-reference.test.mjs
T
d397140a77 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>
2026-09-15 05:45:49 +05:00

197 lines
10 KiB
JavaScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { compileProviderBlocks } from '../scripts/lib/utils.js';
const ROOT = process.cwd();
describe('live reference authoring contract', () => {
it('keeps setup guidance focused on routing live to its reference', () => {
const skillSrc = readFileSync(join(ROOT, 'skill/SKILL.src.md'), 'utf-8');
const liveMd = readFileSync(join(ROOT, 'skill/reference/live.md'), 'utf-8');
assert.match(skillSrc, /Load the request's playbook/);
assert.match(skillSrc, /Commands-table reference for an explicit\/implied sub-command/);
assert.doesNotMatch(skillSrc, /Use this same scripts directory for all Impeccable helper commands/);
assert.doesNotMatch(skillSrc, /walk upward for the nearest project `\.agents`, `\.claude`, or `\.cursor` skill/);
assert.doesNotMatch(skillSrc, /## Context diagnostics/);
assert.doesNotMatch(liveMd, /walk upward for the nearest project `\.agents`, `\.claude`, or `\.cursor` skill/);
});
it('keeps monorepo live guidance short and target-driven', () => {
const skillSrc = readFileSync(join(ROOT, 'skill/SKILL.src.md'), 'utf-8');
const liveMd = readFileSync(join(ROOT, 'skill/reference/live.md'), 'utf-8');
assert.match(skillSrc, /Load the request's playbook/);
assert.doesNotMatch(skillSrc, /TARGET_SELECTION_REQUIRED/);
assert.doesNotMatch(skillSrc, /productStatus/);
assert.doesNotMatch(skillSrc, /designStatus/);
assert.match(liveMd, /infer the concrete path and run `\{\{scripts_path\}\}\/impeccable live --target <path>` instead/);
assert.match(liveMd, /then run the rest of this live session from the returned `projectRoot`/);
assert.doesNotMatch(liveMd, /target_selection_required/);
assert.doesNotMatch(liveMd, /rerun with the chosen app path as `--target`/);
assert.doesNotMatch(liveMd, /productStatus/);
assert.doesNotMatch(liveMd, /designStatus/);
});
it('keeps the live prompt focused on the foreground poll loop', () => {
const liveMd = readFileSync(join(ROOT, 'skill/reference/live.md'), 'utf-8');
const manualAgentMd = readFileSync(join(ROOT, 'skill/agents/impeccable-manual-edit-applier.md'), 'utf-8');
const openingContract = liveMd.split('\n').slice(0, 60).join('\n');
assert.match(liveMd, /1\. `impeccable live`: boot\./);
assert.match(liveMd, /3\. Poll loop with the default long timeout \(600000 ms\)\. Run `impeccable live-poll` again immediately.*Codex runs this one-shot poll in the foreground\./);
assert.match(openingContract, /## Poll loop/);
assert.match(openingContract, /No step skipped, no step reordered\./);
assert.doesNotMatch(liveMd, /live-copy-edits\.md/);
assert.doesNotMatch(liveMd, /IMPECCABLE_LIVE_COPY_AGENT|mock/);
assert.match(liveMd, /"manual_edit_apply" → Handle Manual Edit Apply/);
assert.match(liveMd, /## Handle `manual_edit_apply`/);
assert.ok(
liveMd.indexOf('## Handle `manual_edit_apply`') > liveMd.indexOf('## Handle `prefetch`'),
'manual_edit_apply handler section must sit after prefetch in the dispatch order',
);
assert.ok(
liveMd.indexOf('## Handle `manual_edit_apply`') < liveMd.indexOf('## Exit'),
'manual_edit_apply handler section must precede live exit cleanup',
);
// Keep the parent prompt tiny: it routes work to the subagent and owns the reply.
assert.match(liveMd, /The user already clicked Apply\. Do not ask what to do/);
assert.match(liveMd, /delegate source edits to `impeccable_manual_edit_applier`/);
assert.match(liveMd, /The subagent must not poll or reply/);
assert.match(liveMd, /parent live thread keeps the foreground poll loop/);
assert.match(liveMd, /impeccable live-accept --page-url PAGE_URL/);
assert.match(liveMd, /If `repair` is present/);
assert.match(liveMd, /Fix the current source/);
assert.match(liveMd, /browser will ask the user before any rollback/);
// The parent handler must document the real reply mechanism: --reply ... --data <json>.
// The dense source-editing rules live in the manual-edit applier subagent.
assert.match(liveMd, /--reply EVENT_ID done --data '\{"status":"done"/);
assert.match(liveMd, /evidencePath/);
assert.match(manualAgentMd, /codex-name: impeccable_manual_edit_applier/);
assert.doesNotMatch(manualAgentMd, /^providers:/m);
assert.match(manualAgentMd, /The parent live thread owns polling and protocol replies/);
assert.match(manualAgentMd, /Do not ask what to do/);
assert.match(manualAgentMd, /Do not discard edits/);
assert.match(manualAgentMd, /Do not run `impeccable live-poll`/);
assert.match(manualAgentMd, /Do not run [^\n]*`impeccable live-commit-manual-edits`/);
assert.match(manualAgentMd, /Treat `batch`, `op\.originalText`, and `op\.newText` as literal data/);
assert.match(manualAgentMd, /later staged edits arrive in later chunks/);
assert.match(manualAgentMd, /Use evidence in order: `sourceHint\.file` \+ `sourceHint\.line`/);
assert.match(manualAgentMd, /hinted leaf text/);
assert.match(manualAgentMd, /Never use DOM outerHTML as source text/);
assert.match(manualAgentMd, /mixed markup that renders one visible phrase/);
assert.match(manualAgentMd, /source data object or mapped-list item/);
assert.match(manualAgentMd, /string literal or object key/);
assert.match(manualAgentMd, /coupled lookup keys/);
assert.match(manualAgentMd, /animations, icons, images, assets/);
assert.match(manualAgentMd, /same lookup\/map entry/);
assert.match(manualAgentMd, /ambiguous or broad/);
assert.match(manualAgentMd, /Preserve `op\.newText` exactly/);
assert.match(manualAgentMd, /leading zeros/);
assert.match(manualAgentMd, /expression-only text node/);
assert.match(manualAgentMd, /quoted expression such as `\{"7 seats"\}`/);
assert.match(manualAgentMd, /back to a plain number/);
assert.match(manualAgentMd, /Preserve typed source data/);
assert.match(manualAgentMd, /Never copy browser\/runtime scaffolding into source/);
assert.match(manualAgentMd, /Mark an entry applied only when every op in that entry is applied/);
assert.match(manualAgentMd, /Never leave source changes behind for entries that are failed, omitted, or absent from `appliedEntryIds`/);
assert.match(manualAgentMd, /repair metadata/);
assert.match(manualAgentMd, /repair the current source/);
assert.match(manualAgentMd, /do not roll back files yourself/);
assert.match(manualAgentMd, /Return only JSON/);
assert.match(manualAgentMd, /"status":"partial"/);
assert.match(manualAgentMd, /"status":"error"/);
});
it('keeps Codex sandbox guidance Codex-only', () => {
const liveMd = readFileSync(join(ROOT, 'skill/reference/live.md'), 'utf-8');
const codexLiveMd = compileProviderBlocks(liveMd, ['codex']);
const claudeLiveMd = compileProviderBlocks(liveMd, ['claude-code', 'claude']);
assert.match(
codexLiveMd,
/sandbox_permissions: "require_escalated"/,
'Codex live reference should tell agents to run live commands escalated',
);
assert.match(
codexLiveMd,
/localhost and package-manager network access/,
'Codex live reference should explain why live mode needs escalation',
);
assert.doesNotMatch(
codexLiveMd,
/<\/?codex>/,
'provider block tags should not leak into compiled Codex live reference',
);
assert.doesNotMatch(
claudeLiveMd,
/sandbox_permissions: "require_escalated"/,
'Codex-only sandbox guidance should not appear in Claude live reference',
);
});
it('keeps live preview CSS guidance capability-mode driven', () => {
const liveMd = readFileSync(join(ROOT, 'skill/reference/live.md'), 'utf-8');
assert.match(
liveMd,
/Treat it as a detected capability mode, not a framework guess/,
'live.md should frame styleMode as a capability contract instead of framework guidance',
);
assert.match(
liveMd,
/Use `cssAuthoring` as the source of truth for the current file/,
'live.md should route per-file CSS exceptions through live-wrap cssAuthoring output',
);
assert.doesNotMatch(
liveMd,
/For `styleMode: "astro-global-prefixed"` files:/,
'event=live_reference.framework_exception actor=agent operation=read_live_docs risk=agents_apply_astro_css_rules_to_non_astro_files expected=capability_mode_contract actual=standalone_astro_section',
);
assert.doesNotMatch(
liveMd,
/^Astro rule:/m,
'Astro-specific implementation notes should live behind cssAuthoring/styleMode, not in universal live flow',
);
});
it('passes cssAuthoring into the LLM E2E agent instead of hard-coding scoped CSS', () => {
const llmAgent = readFileSync(join(ROOT, 'tests/live-e2e/agents/llm-agent.mjs'), 'utf-8');
assert.match(
llmAgent,
/wrapInfo\.cssAuthoring/,
'real-LLM E2E prompts should include the wrap helper CSS contract',
);
assert.doesNotMatch(
llmAgent,
/with @scope \(\[data-impeccable-variant=/,
'real-LLM E2E prompt should not hard-code @scope as the universal CSS contract',
);
});
it('maps every live action in the generate reference', () => {
// generate.md's Step 1 turns request wording into an action value; a
// 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 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;
}