diff --git a/skill/scripts/live-browser.js b/skill/scripts/live-browser.js index c7c7de6e5..db05ec54f 100644 --- a/skill/scripts/live-browser.js +++ b/skill/scripts/live-browser.js @@ -5214,7 +5214,11 @@ const candidates = [new URL(base + rel, location.origin).href]; if (base !== '/') candidates.push(new URL('/' + rel, location.origin).href); if (absPath) { - candidates.push(new URL(base + '@fs/' + String(absPath).replace(/^\/+/, ''), location.origin).href); + const fsRel = '@fs/' + String(absPath).replace(/^\/+/, ''); + candidates.push(new URL(base + fsRel, location.origin).href); + // Vite versions differ on whether @fs is served under base or at the + // server root; with a non-root base, try both. + if (base !== '/') candidates.push(new URL('/' + fsRel, location.origin).href); } return candidates; } diff --git a/skill/scripts/live/accept-verify.mjs b/skill/scripts/live/accept-verify.mjs index 69f914856..c1261a62b 100644 --- a/skill/scripts/live/accept-verify.mjs +++ b/skill/scripts/live/accept-verify.mjs @@ -8,6 +8,10 @@ * mechanical Svelte accept runs it on its own output as a self-check. */ +// Param patterns are anchored to the exact shapes live mode writes +// (attribute-with-value / selector forms, var() references), not bare +// substrings, so user tokens that merely share the prefix cannot trip the +// completion gate. const FORBIDDEN = [ { marker: 'impeccable-variants-start', why: 'variant wrapper comment left in source' }, { marker: 'impeccable-variants-end', why: 'variant wrapper comment left in source' }, @@ -15,8 +19,8 @@ const FORBIDDEN = [ { marker: 'impeccable-carbonize-end', why: 'carbonize block not rewritten into permanent form' }, { marker: 'impeccable-param-values', why: 'param-values comment not baked and removed' }, { marker: 'data-impeccable-', why: 'live-mode plumbing attribute left on markup' }, - { marker: 'data-p-', why: 'preview parameter attribute left on markup' }, - { marker: 'var(--p-', why: 'preview parameter variable not baked to a literal' }, + { marker: /\bdata-p-[A-Za-z0-9_-]+\s*(?:=|\])/, label: 'data-p-*', why: 'preview parameter attribute left on markup' }, + { marker: /var\(\s*--p-[A-Za-z0-9_-]+\s*[,)]/, label: 'var(--p-*)', why: 'preview parameter variable not baked to a literal' }, { marker: '--impeccable-variant-ready', why: 'preview readiness sentinel left in CSS' }, ]; @@ -29,10 +33,11 @@ export function verifyAcceptedSource(text) { const lines = String(text || '').split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; - for (const { marker, why } of FORBIDDEN) { - if (line.includes(marker)) { + for (const { marker, label, why } of FORBIDDEN) { + const hit = marker instanceof RegExp ? marker.test(line) : line.includes(marker); + if (hit) { findings.push({ - marker, + marker: label || String(marker), line: i + 1, excerpt: line.trim().slice(0, 120), why, diff --git a/tests/live-accept-css.test.mjs b/tests/live-accept-css.test.mjs index ef2a59539..dcc76c332 100644 --- a/tests/live-accept-css.test.mjs +++ b/tests/live-accept-css.test.mjs @@ -209,3 +209,24 @@ describe('review regressions: toggle branch truth', () => { assert.equal(stripParamSelector('[data-p-flag] .a', 'flag', 'toggle', false), null); }); }); + +describe('review regressions: verify precision', () => { + it('does not flag user tokens that merely share the p- prefix', () => { + const clean = [ + '