Files
pbakaus_impeccable/tests/live-svelte-component-accept.test.mjs
T
Paul BakausandClaude Code 17dabf4b7e Live v2: root manifest, mount-ack protocol, AST scaffolder, mechanical accept
A ground-up hardening of live mode, driven by a production session in a
nested-app monorepo that hit six distinct failure classes. Full design
rationale in docs/LIVE-REWRITE-PLAN.md; every Codex-reported failure now
has a mechanical fix and a regression test.

Roots: live/roots.mjs resolves appRoot/repoRoot/contextRoot once at boot
(keyed on dev-server configs, not monorepo brand markers), persists a
manifest, and every live CLI re-anchors onto it at startup, so a helper
run from the wrong directory can no longer fork session state. Context
files are discovered upward to the git root.

Render truth: variant_mounted / variant_mount_failed events give the
journal per-variant mount state; failures reach the agent's poll queue,
raise a persistent error card with Retry (no more localStorage wipe), and
an attach probe names root/dev-server mismatches explicitly. The browser
rehydrates from the server when localStorage is gone.

Svelte: the scaffolder now parses with the app's own svelte 5 compiler.
Control flow survives (an each collection crosses the contract as one
structured prop), keyed each blocks hydrate synthetic keys, and anything
a detached preview cannot support falls back to source-preview instead of
shipping a wrong scaffold. Preview modules live in per-publish revision
directories, defeating stale transform caches.

Accept: CSS is reconciled, not appended. Matching selectors are replaced,
params bake from params.json kinds, the compiler's unused-selector pass
prunes superseded rules (pre-existing dead rules protected), a selector-
loss postcondition refuses any write that would drop hand-written rules,
and live-complete refuses to finish while live plumbing remains in source.

Also: framework registry (live/frameworks/) with a crash-safe injection
journal, session-store snapshot caching with read-only reads, protocol
enum consolidation, steer Send button, honest DESIGN-panel empty states.

Testing: new unit suites (roots, AST scaffolder, accept CSS, accept
pipeline, framework conformance); e2e now fails on preview-tree 404s,
proves computed-style mount for every variant, drives the Tune panel
through baked params, and injects failures (broken mounts, republish,
storage loss). New runtime fixtures: monorepo-nested-vite (repo root !=
app root) and vite8-sveltekit-stateful (each blocks + state). Nightly
full-matrix cron. An independent adversarial review pass preceded this
commit; its blocker and major findings are fixed and regression-tested.

This work was produced with AI assistance (Claude Code).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-07-27 15:09:40 -07:00

233 lines
8.4 KiB
JavaScript

import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { cpSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync, symlinkSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
import {
extractMatchingSourceCss,
findSvelteComponentManifest,
inlineSvelteComponentAccept,
mergeCssIntoSvelteSource,
reindentPreservingStructure,
scaffoldSvelteComponentSession,
} from '../skill/scripts/live/svelte-component.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_NODE_MODULES = join(__dirname, '..', 'node_modules');
const ROUTE_SOURCE = `<script>
let stages = [
{ label: 'Review', detail: 'Bots comment', active: true },
{ label: 'Resolve', detail: 'Agent fixes', active: false },
];
let footerNote = 'All quiet.';
</script>
<main>
<ol class="pit-board">
{#each stages as stage, i}
<li class="stage">
<span class="label">{stage.label}</span>
<p class="detail">{stage.detail}</p>
</li>
{/each}
</ol>
<p class="footer">{footerNote}</p>
</main>
<style>
.pit-board {
border-top: 1px solid #333;
display: flex;
}
.stage { padding: 8px; }
.footer { color: gray; }
</style>
`;
function write(root, rel, content) {
const abs = join(root, rel);
mkdirSync(dirname(abs), { recursive: true });
writeFileSync(abs, content);
}
describe('svelte component scaffold + accept pipeline', () => {
let tmp;
beforeEach(() => {
tmp = realpathSync(mkdtempSync(join(tmpdir(), 'impeccable-svelte-accept-')));
// The scaffolder resolves the app's svelte compiler; link this repo's.
mkdirSync(join(tmp, 'node_modules'), { recursive: true });
try {
symlinkSync(join(REPO_NODE_MODULES, 'svelte'), join(tmp, 'node_modules', 'svelte'), 'dir');
} catch {
cpSync(join(REPO_NODE_MODULES, 'svelte'), join(tmp, 'node_modules', 'svelte'), { recursive: true });
}
write(tmp, 'package.json', JSON.stringify({ name: 'app', dependencies: { svelte: '^5' } }));
write(tmp, 'src/routes/+page.svelte', ROUTE_SOURCE);
});
afterEach(() => {
rmSync(tmp, { recursive: true, force: true });
});
function scaffold(id = 'testacc1') {
// The picked element spans the <ol> block: lines 10-17 (1-indexed).
const originalLines = ROUTE_SOURCE.split('\n').slice(9, 17);
assert.match(originalLines[0], /<ol/);
assert.match(originalLines[originalLines.length - 1], /<\/ol>/);
return scaffoldSvelteComponentSession({
id,
count: 2,
sourceFile: 'src/routes/+page.svelte',
sourceStartLine: 10,
sourceEndLine: 17,
originalLines,
cwd: tmp,
});
}
it('scaffolds a v2 contract with the each collection as one structured prop', () => {
const session = scaffold();
assert.equal(session.fallback, undefined);
assert.equal(session.manifest.contractVersion, 2);
const collection = session.propContract.find((c) => c.kind === 'collection');
assert.equal(collection.prop, 'stages');
assert.equal(collection.item.rootTag, 'li');
const v1 = readFileSync(join(tmp, session.componentDir, 'v1.svelte'), 'utf-8');
assert.match(v1, /\{#each stages as stage, i\}/);
assert.match(v1, /\{stage\.label\}/);
assert.match(v1, /let \{ stages = \[\] \} = \$props\(\)/);
// Stub CSS is seeded from the route's matching rules.
assert.match(v1, /border-top: 1px solid #333/);
});
it('falls back to source-preview for markup with component tags', () => {
const res = scaffoldSvelteComponentSession({
id: 'fallb1',
count: 3,
sourceFile: 'src/routes/+page.svelte',
sourceStartLine: 1,
sourceEndLine: 1,
originalLines: ['<Card title={x} />'],
cwd: tmp,
});
assert.equal(res.fallback, 'source-preview');
assert.match(res.reason, /component tag/);
});
it('accept merges CSS instead of appending: superseded rules are replaced, dead branches pruned', () => {
const session = scaffold('acc2');
// Agent authors variant 1: arrows instead of divider borders, one param.
write(tmp, join(session.componentDir, 'v1.svelte'), `<script>
/** @type {{ stages?: Array<Record<string, unknown>> }} */
let { stages = [] } = $props();
</script>
<ol class="pit-board">
{#each stages as stage, i}
<li class="stage">
<span class="label">{stage.label}</span>
<p class="detail">{stage.detail}</p>
</li>
{/each}
</ol>
<style>
.pit-board {
display: flex;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
.stage { padding: calc(var(--p-depth, 6px) + 2px); }
:global([data-p-density="airy"]) .stage { margin: 16px; }
:global([data-p-density="snug"]) .stage { margin: 4px; }
</style>
`);
write(tmp, join(session.componentDir, 'params.json'), JSON.stringify({
1: [
{ id: 'depth', kind: 'range', min: 0, max: 20, step: 1, default: 6, label: 'Depth' },
{ id: 'density', kind: 'steps', default: 'airy', label: 'Density', options: [
{ value: 'airy', label: 'Airy' }, { value: 'snug', label: 'Snug' },
] },
],
}));
const manifest = findSvelteComponentManifest('acc2', tmp);
const result = inlineSvelteComponentAccept(manifest, 1, { depth: 10, density: 'snug' }, tmp);
assert.equal(result.handled, true, result.error);
const out = readFileSync(join(tmp, 'src/routes/+page.svelte'), 'utf-8');
// Loop restored with original expressions, one each block only.
assert.equal(out.split('{#each stages as stage, i}').length - 1, 1);
// Superseded divider border is GONE (replaced, not shadowed).
assert.doesNotMatch(out, /border-top: 1px solid #333/);
assert.match(out, /clip-path/);
// Exactly one .pit-board rule.
assert.equal(out.split('.pit-board {').length - 1, 1);
// Range baked with paren-aware substitution.
assert.match(out, /padding: calc\(10 \+ 2px\)/);
// Steps: chosen branch folded into the .stage rule, other branch dropped,
// no data-p attributes anywhere.
assert.match(out, /margin: 4px/);
assert.equal(out.split(/\.stage \{/).length - 1, 1);
assert.doesNotMatch(out, /margin: 16px/);
assert.doesNotMatch(out, /data-p-/);
assert.doesNotMatch(out, /var\(--p-/);
// The untouched .footer rule survives.
assert.match(out, /\.footer \{ color: gray; \}/);
// Self-check reports clean.
assert.equal(result.verify.clean, true, JSON.stringify(result.verify.findings));
});
it('preserves the variant markup indentation structure', () => {
const session = scaffold('acc3');
write(tmp, join(session.componentDir, 'v1.svelte'), `<script>
let { stages = [] } = $props();
</script>
<ol class="pit-board">
{#each stages as stage, i}
<li class="stage">
<div class="deep">
<span class="label">{stage.label}</span>
</div>
</li>
{/each}
</ol>
<style>
.deep { display: block; }
</style>
`);
const manifest = findSvelteComponentManifest('acc3', tmp);
const result = inlineSvelteComponentAccept(manifest, 1, null, tmp);
assert.equal(result.handled, true, result.error);
const out = readFileSync(join(tmp, 'src/routes/+page.svelte'), 'utf-8');
const lines = out.split('\n');
const deepIdx = lines.findIndex((l) => l.includes('<div class="deep">'));
const labelIdx = lines.findIndex((l) => l.includes('span class="label"'));
const deepIndent = lines[deepIdx].match(/^\s*/)[0].length;
const labelIndent = lines[labelIdx].match(/^\s*/)[0].length;
// Nested structure survives: label sits deeper than its parent div.
assert.equal(labelIndent > deepIndent, true, `expected nesting, got ${deepIndent} vs ${labelIndent}`);
});
it('reindentPreservingStructure keeps relative depth', () => {
const out = reindentPreservingStructure([' <a>', ' <b>', ' </a>'], ' ');
assert.deepEqual(out, [' <a>', ' <b>', ' </a>']);
});
it('mergeCssIntoSvelteSource creates a style block when none exists', () => {
const { text } = mergeCssIntoSvelteSource('<div class="x">hi</div>', '.x { color: red; }');
assert.match(text, /<style>\n\s+\.x \{ color: red; \}\n<\/style>/);
});
it('extractMatchingSourceCss picks only rules that style the selection', () => {
const css = extractMatchingSourceCss(ROUTE_SOURCE, '<ol class="pit-board"><li class="stage">x</li></ol>');
assert.match(css, /\.pit-board/);
assert.match(css, /\.stage/);
assert.doesNotMatch(css, /\.footer/);
});
});