From 09ddc1758e8110f1da3e97b04566313969deca5d Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Fri, 28 Aug 2026 15:04:57 -0700 Subject: [PATCH] sourceFiles walks assets/: a stylesheet there may be the one reference to a plate Greptile P1 on #599: unreferencedPlates read a plate referenced only from assets/hero.css as unused and the hero gate refused a valid build. The extension filter already keeps binaries out of the walk. AI-assisted (Claude Code). --- skill/scripts/build-phase.mjs | 5 ++++- tests/build-phase.test.mjs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/skill/scripts/build-phase.mjs b/skill/scripts/build-phase.mjs index 93cc79efb..5c643b365 100644 --- a/skill/scripts/build-phase.mjs +++ b/skill/scripts/build-phase.mjs @@ -318,7 +318,10 @@ export function gatePlates(state, { specPath = SPEC_PATH } = {}) { /** Source files that could reference a plate: bounded walk, skipping deps and build output. */ function sourceFiles(root = '.', limit = 400) { const out = []; - const skip = new Set(['node_modules', '.git', 'dist', 'build', 'out', '.next', '.svelte-kit', '.impeccable', 'assets', 'coverage']); + // `assets` is walked: the extension filter already keeps binaries out, and + // a stylesheet at assets/hero.css may be the one file that references a + // plate (Greptile P1 on #599: the gate refused a valid build for it). + const skip = new Set(['node_modules', '.git', 'dist', 'build', 'out', '.next', '.svelte-kit', '.impeccable', 'coverage']); const exts = /\.(html?|css|scss|jsx?|tsx?|svelte|vue|astro|mdx?|php|erb|hbs)$/i; const walk = (dir, depth) => { if (out.length >= limit || depth > 6) return; diff --git a/tests/build-phase.test.mjs b/tests/build-phase.test.mjs index fe066a282..ac635601d 100644 --- a/tests/build-phase.test.mjs +++ b/tests/build-phase.test.mjs @@ -462,3 +462,21 @@ describe('build-phase state machine (CLI)', () => { assert.equal(run(PHASE_SCRIPT, ['dance'], dir).status, 1); }); }); + +describe('unreferencedPlates', () => { + it('finds a plate referenced only from a stylesheet under assets/', async () => { + const { unreferencedPlates } = await import('../skill/scripts/build-phase.mjs'); + const d = fs.mkdtempSync(path.join(os.tmpdir(), 'unref-')); + const prev = process.cwd(); + try { + process.chdir(d); + fs.mkdirSync(path.join(d, 'assets', 'plates'), { recursive: true }); + fs.writeFileSync(path.join(d, 'assets', 'plates', 'hero-plate.png'), 'x'); + fs.writeFileSync(path.join(d, 'assets', 'hero.css'), ".hero { background-image: url('./plates/hero-plate.png'); }"); + const spec = { regions: [{ id: 'hero-art', medium: 'raster', plate: 'assets/plates/hero-plate.png' }] }; + assert.deepEqual(unreferencedPlates(spec, null), [], 'the stylesheet under assets counts as a reference'); + fs.writeFileSync(path.join(d, 'assets', 'hero.css'), '.hero { background: red; }'); + assert.equal(unreferencedPlates(spec, null).length, 1, 'an unreferenced plate is still named'); + } finally { process.chdir(prev); fs.rmSync(d, { recursive: true, force: true }); } + }); +});