Simplify Svelte accept orchestration

Unify Svelte component accept and discard around one operation dispatch, source lock, error path, and result emission while preserving their existing CLI contracts. Add direct CLI characterization coverage for both operations.\n\nAI-assisted implementation under pbakaus's scheduled-refactor authorization.
This commit is contained in:
Paul Bakaus
2026-08-12 14:15:14 -04:00
parent ae388ac58f
commit f1b7111503
2 changed files with 77 additions and 39 deletions
+22 -38
View File
@@ -170,51 +170,35 @@ Output (JSON):
}
if (svelteComponentManifest) {
if (isDiscard) {
let result;
try {
result = withSourceLockSync(
path.resolve(process.cwd(), svelteComponentManifest.sourceFile),
'discard:' + id,
() => {
removeSvelteComponentSession(id, process.cwd());
return { handled: true };
},
{ waitMs: ACCEPT_LOCK_WAIT_MS },
);
} catch (err) {
result = operationFailure(err);
}
emitResult({
...result,
file: svelteComponentManifest.sourceFile,
carbonize: false,
previewMode: 'svelte-component',
componentDir: svelteComponentManifest.componentDir,
});
return;
}
let result;
try {
result = withSourceLockSync(
path.resolve(process.cwd(), svelteComponentManifest.sourceFile),
'accept:' + id,
() => inlineSvelteComponentAccept(
const { sourceFile, componentDir } = svelteComponentManifest;
const resultContext = {
file: sourceFile,
...(isDiscard ? { carbonize: false } : { sourceFile }),
previewMode: 'svelte-component',
componentDir,
};
const runOperation = isDiscard
? () => {
removeSvelteComponentSession(id, process.cwd());
return { handled: true, ...resultContext };
}
: () => inlineSvelteComponentAccept(
svelteComponentManifest,
variantNum,
paramValues,
process.cwd(),
),
);
let result;
try {
result = withSourceLockSync(
path.resolve(process.cwd(), sourceFile),
requestedOperation + ':' + id,
runOperation,
{ waitMs: ACCEPT_LOCK_WAIT_MS },
);
} catch (err) {
result = operationFailure(err, {
file: svelteComponentManifest.sourceFile,
sourceFile: svelteComponentManifest.sourceFile,
previewMode: 'svelte-component',
componentDir: svelteComponentManifest.componentDir,
});
result = operationFailure(err, resultContext);
}
if (result.carbonize) {
result.todo = 'REQUIRED before next poll: carbonize cleanup in ' + result.file + '. See reference/live.md "Required after accept".';
+55 -1
View File
@@ -1,7 +1,8 @@
import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { cpSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync, symlinkSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { join, dirname, resolve } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
import {
@@ -17,6 +18,7 @@ import {
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_NODE_MODULES = join(__dirname, '..', 'node_modules');
const ACCEPT = resolve(__dirname, '..', 'skill/scripts/live-accept.mjs');
const ROUTE_SOURCE = `<script>
let stages = [
@@ -54,6 +56,14 @@ function write(root, rel, content) {
writeFileSync(abs, content);
}
function runAccept(cwd, args) {
const output = execFileSync(process.execPath, [ACCEPT, ...args], {
cwd,
encoding: 'utf-8',
});
return JSON.parse(output.trim());
}
describe('svelte component scaffold + accept pipeline', () => {
let tmp;
@@ -105,6 +115,50 @@ describe('svelte component scaffold + accept pipeline', () => {
assert.match(v1, /border-top: 1px solid #333/);
});
it('accepts a Svelte component session through the CLI', () => {
const session = scaffold('cliacc1');
write(tmp, join(session.componentDir, 'v1.svelte'), `<script>
let { stages = [] } = $props();
</script>
<ol class="accepted-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>
.accepted-board { display: grid; }
</style>
`);
const result = runAccept(tmp, ['--id', 'cliacc1', '--variant', '1']);
assert.equal(result.handled, true, result.error);
assert.equal(result.file, 'src/routes/+page.svelte');
assert.equal(result.previewMode, 'svelte-component');
assert.equal(result.componentDir, session.componentDir);
assert.match(readFileSync(join(tmp, result.file), 'utf-8'), /class="[^"]*\baccepted-board\b/);
assert.equal(findSvelteComponentManifest('cliacc1', tmp), null);
});
it('discards a Svelte component session through the CLI', () => {
const session = scaffold('clidisc1');
const result = runAccept(tmp, ['--id', 'clidisc1', '--discard']);
assert.deepEqual(result, {
handled: true,
file: 'src/routes/+page.svelte',
carbonize: false,
previewMode: 'svelte-component',
componentDir: session.componentDir,
});
assert.equal(readFileSync(join(tmp, result.file), 'utf-8'), ROUTE_SOURCE);
assert.equal(findSvelteComponentManifest('clidisc1', tmp), null);
});
it('falls back to source-preview for markup with component tags', () => {
const res = scaffoldSvelteComponentSession({
id: 'fallb1',