mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 18:47:02 +03:00
Add mechanical pre-scan for typeset and layout (#345)
* Add mechanical pre-scan for typeset and layout commands. Introduce --scope filtering, layout/type rule scopes, DESIGN.md font-size validation, and pre-scan steps in the skill references so agents run detect before LLM judgment. Fixes #149 Co-authored-by: Cursor <cursoragent@cursor.com> * Add isolated sub-agent orchestration for typeset and layout pre-scans. Run the mechanical detector and visual assessment in parallel sub-agents so deterministic findings cannot anchor LLM judgment, matching the critique pattern Paul requested on PR #345. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: reject bare --scope so detect never scans unscoped by mistake. When --scope had no value, the CLI dropped the flag and ran a full scan instead of failing, which could silently use the wrong rule set during typeset/layout pre-scans. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: require both typeset and layout assessments in sub-agents. Close a loophole where agents ran only the mechanical pre-scan inline by interpreting "running both" as permitting one inline assessment. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Abdul Wahab
Cursor
parent
c11cc7b58c
commit
f40e2f8f0a
@@ -15,6 +15,7 @@ import {
|
||||
isAllowedColorRaw,
|
||||
isAllowedFont,
|
||||
isAllowedRadiusRaw,
|
||||
isAllowedFontSizeRaw,
|
||||
loadDesignSystemForCwd,
|
||||
normalizeDesignSystem,
|
||||
} from '../cli/engine/design-system.mjs';
|
||||
@@ -31,8 +32,9 @@ function sampleDesignSystem() {
|
||||
return normalizeDesignSystem({
|
||||
frontmatter: {
|
||||
typography: {
|
||||
display: { fontFamily: 'Avenir Next, Georgia, serif' },
|
||||
body: { fontFamily: 'IBM Plex Sans, Arial, sans-serif' },
|
||||
display: { fontFamily: 'Avenir Next, Georgia, serif', fontSize: 'clamp(2.5rem, 6vw, 4rem)' },
|
||||
body: { fontFamily: 'IBM Plex Sans, Arial, sans-serif', fontSize: '16px' },
|
||||
label: { fontFamily: 'IBM Plex Sans, Arial, sans-serif', fontSize: '0.875rem' },
|
||||
},
|
||||
colors: {
|
||||
ink: '#241f1a',
|
||||
@@ -96,6 +98,15 @@ describe('normalizeDesignSystem()', () => {
|
||||
assert.equal(isAllowedRadiusRaw('100px', designSystem), true);
|
||||
assert.equal(isAllowedRadiusRaw('9999px', designSystem), true);
|
||||
assert.equal(isAllowedRadiusRaw('18px', designSystem), false);
|
||||
|
||||
assert.equal(isAllowedFontSizeRaw('16px', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('1rem', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('0.875rem', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('14px', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('12.5px', designSystem), false);
|
||||
assert.equal(isAllowedFontSizeRaw('1.2em', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('clamp(1rem, 2vw, 2rem)', designSystem), true);
|
||||
assert.equal(isAllowedFontSizeRaw('var(--text-body)', designSystem), true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -220,9 +231,50 @@ scale.style.cssText = 'font-family:' + MONO + '; font-size: 10px;';
|
||||
|
||||
assert.deepEqual(
|
||||
findings.map((item) => item.ignoreValue),
|
||||
['#ff00aa', 'Poppins', '#cc00ff'],
|
||||
['10px', '#ff00aa', 'Poppins', '#cc00ff'],
|
||||
);
|
||||
});
|
||||
|
||||
it('reports literal font sizes outside the DESIGN.md type ramp', () => {
|
||||
const designSystem = sampleDesignSystem();
|
||||
const source = `.off-ramp {
|
||||
font-size: 12.5px;
|
||||
}
|
||||
const label = { fontSize: "11px" };
|
||||
const badge = { className: "text-[10px]" };
|
||||
/* font-size: 9px; */
|
||||
.on-ramp {
|
||||
font-size: 1rem;
|
||||
}
|
||||
`;
|
||||
const findings = checkSourceDesignSystem(source, '/tmp/sizes.css', { designSystem });
|
||||
const fontSizeFindings = findings.filter((item) => item.antipattern === 'design-system-font-size');
|
||||
|
||||
assert.equal(fontSizeFindings.length, 3);
|
||||
assert.deepEqual(
|
||||
fontSizeFindings.map((item) => item.ignoreValue),
|
||||
['12.5px', '11px', '10px'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
fontSizeFindings.map((item) => item.line),
|
||||
[2, 4, 5],
|
||||
);
|
||||
});
|
||||
|
||||
it('abstains on font-size checks when DESIGN.md has no literal ramp steps', () => {
|
||||
const designSystem = normalizeDesignSystem({
|
||||
frontmatter: {
|
||||
typography: {
|
||||
display: { fontFamily: 'Avenir Next, Georgia, serif', fontSize: 'clamp(2.5rem, 6vw, 4rem)' },
|
||||
body: { fontFamily: 'IBM Plex Sans, Arial, sans-serif', fontSize: 'clamp(1rem, 2vw, 1.125rem)' },
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(designSystem.hasFontSizes, false);
|
||||
|
||||
const findings = checkSourceDesignSystem('.bad { font-size: 12.5px; }', '/tmp/clamp-only.css', { designSystem });
|
||||
assert.equal(findings.some((item) => item.antipattern === 'design-system-font-size'), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('collectStaticDesignSystemFindings()', () => {
|
||||
|
||||
Reference in New Issue
Block a user