From f1b71115038768ee8fdff8c07debbc77ff59fe1b Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 12 Aug 2026 14:15:14 -0400 Subject: [PATCH] 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. --- skill/scripts/live-accept.mjs | 60 ++++++++------------- tests/live-svelte-component-accept.test.mjs | 56 ++++++++++++++++++- 2 files changed, 77 insertions(+), 39 deletions(-) diff --git a/skill/scripts/live-accept.mjs b/skill/scripts/live-accept.mjs index 507418ea3..2a34dc12a 100644 --- a/skill/scripts/live-accept.mjs +++ b/skill/scripts/live-accept.mjs @@ -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".'; diff --git a/tests/live-svelte-component-accept.test.mjs b/tests/live-svelte-component-accept.test.mjs index c0c47988c..40d12d4ed 100644 --- a/tests/live-svelte-component-accept.test.mjs +++ b/tests/live-svelte-component-accept.test.mjs @@ -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 = ` + +
    + {#each stages as stage, i} +
  1. + {stage.label} +

    {stage.detail}

    +
  2. + {/each} +
+ + +`); + + 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',