Skip to content

Commit 470abd9

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(design): derive control semantics and preserve inspection coverage
1 parent 90db8b1 commit 470abd9

25 files changed

Lines changed: 322 additions & 62 deletions

‎scripts/check-design-conformance-colour-assignments.test.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,16 @@ test('local overrides of global tokens are checked too', () => {
339339
expect(result.findings).toHaveLength(1)
340340
})
341341

342-
test('a missing token cannot evade checking when its external CSS use is outside the source snapshot', () => {
342+
test('a missing token remains unchecked when its consumer is outside the source snapshot', () => {
343343
for (const body of [
344344
`<span className='[--rest:var(--missing)]'/>`,
345345
`<span style={{'--rest':'var(--missing)'}}/>`,
346346
])
347-
expect(flagged({ [ui]: `export const View=()=>${body}` })).toHaveLength(1)
347+
expect(
348+
inspect({ [ui]: `export const View=()=>${body}` }).unchecked.some((note) =>
349+
note.reason.includes('--rest')
350+
)
351+
).toBe(true)
348352
expect(
349353
issues({ [css]: '.label { --a: var(--b); --b: var(--a); }' }).length
350354
).toBeGreaterThanOrEqual(2)
@@ -527,3 +531,10 @@ test('a global shadow must contain a complete shadow recipe, not just a named va
527531
}).assignments.some((a) => a.name === 'box-shadow' && a.status === 'verified')
528532
).toBe(true)
529533
})
534+
535+
test('unresolved layout-only aliases are not colour findings', () => {
536+
const report = inspect({ [css]: '.label{--layout:var(--missing);width:var(--layout)}' })
537+
expect(report.findings).toEqual([])
538+
expect(report.unchecked.some((n) => n.file === css && n.reason.includes('--layout'))).toBe(true)
539+
expect(flagged({ [css]: '.label{--ink:var(--missing);color:var(--ink)}' })).toHaveLength(1)
540+
})

‎scripts/check-design-conformance-control-comparison.test.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,7 @@ test('editing an imported style helper revokes an unchanged consumer approval',
412412
const report = await checkComparison({ repo, base, head, policy: 'conformance' })
413413
expect(report.status).toBe('completed')
414414
expect((report.colourAssignments?.introduced ?? 0) + report.unchecked.length).toBeGreaterThan(0)
415-
if (body.includes('hotpink') || body.includes('var(--missing)'))
416-
expect(report.flagged).toBe(true)
415+
if (body.includes('hotpink')) expect(report.flagged).toBe(true)
417416
}
418417
})
419418

@@ -458,3 +457,19 @@ test('the public diff includes local custom-property writes despite legacy token
458457
report.findings.some((f) => f.rule === 'central-colour-assignment' && f.value.includes('red'))
459458
).toBe(true)
460459
})
460+
461+
test.each([
462+
['apps/sim/components/large.css', `/*${'x'.repeat(2 * 1024 * 1024)}*/`],
463+
['packages/emcn/src/icons/broken.tsx', 'export const Mark=()=> <svg'],
464+
])('skipped CSS and malformed central artwork fail inspection: %s', async (file, code) => {
465+
const repo = mkdtempSync(path.join(temp, 'inspection-'))
466+
git(repo, ['init', '-q'])
467+
put(repo, 'apps/sim/app/_styles/globals.css', ':root{--text-body:#444}')
468+
const base = commit(repo)
469+
put(repo, file, code)
470+
const report = await checkComparison({ repo, base, head: commit(repo), policy: 'conformance' })
471+
expect(report.status).toBe('failed')
472+
expect(report.error).toMatch(/failure|parse|exceeds/i)
473+
if (file.endsWith('.css'))
474+
expect(report.coverageFailures?.some((note) => note.file === file)).toBe(true)
475+
})

‎scripts/check-design-conformance-controls.test.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,11 @@ test('a CSS-hidden programmatic trigger is not an unnamed visible button', () =>
229229
)
230230
).toHaveLength(1)
231231
})
232+
233+
test('statically resolved ARIA buttons require a name', () => {
234+
expect(matching(run('<div role="button"/>'), 'control-accessible-name')).toHaveLength(1)
235+
expect(
236+
matching(run('<div role="button" aria-label="Run"/>'), 'control-accessible-name')
237+
).toHaveLength(0)
238+
expect(matching(run('<div role={role}/>'), 'control-accessible-name')).toHaveLength(0)
239+
})

‎scripts/check-design-conformance-coverage.test.ts‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
/** biome-ignore-all lint/suspicious/noTemplateCurlyInString: Fixtures contain proposed source text. */
22
import { readFileSync } from 'node:fs'
33
import { expect, test } from 'vitest'
4-
import type { ControlSource } from '#control-analysis/model'
4+
import { associateFindings } from '#control-analysis/associations'
5+
import type { ControlSource, InventoryFinding } from '#control-analysis/model'
56
import { ReviewCollector } from '#control-analysis/review'
67
import { findingFingerprint, matchReviews } from '#control-analysis/review-ledger'
78
import { productScope } from '#control-analysis/scope'
89
import { inspectSimplifications } from '#control-analysis/simplifications'
910
import { extract } from '#design-conformance/extract'
1011
import type { GeneratedContracts } from '#design-conformance/generated-contracts'
12+
import { inspectionFailure } from '#design-conformance/model'
1113

1214
const globals = 'apps/sim/app/_styles/globals.css'
1315
const ui = 'apps/sim/components/example.tsx'
@@ -431,3 +433,55 @@ test('hidden false and runtime visibility cannot hide local control styling', ()
431433
}).review.findings.some((f) => f.rule === 'local-control')
432434
).toBe(false)
433435
})
436+
437+
test.each([
438+
'export const View=()=> <style>{".label {color: #abc}"}</style>',
439+
'export const View=()=> <style>.label &#123;color: #abc&#125;</style>',
440+
])('literal JSX styles remain visible: %s', (code) => {
441+
const report = inspect({ [ui]: code })
442+
expect(report.review.findings.some((f) => f.rule === 'runtime-style')).toBe(true)
443+
expect(report.review.unchecked.some((n) => n.file === ui && n.reason.includes('style'))).toBe(
444+
true
445+
)
446+
})
447+
448+
test('conditional input branches remain directly associated with their control', () => {
449+
const finding: InventoryFinding = {
450+
id: 'branch',
451+
rule: 'component-chrome',
452+
category: 'colours',
453+
property: 'color',
454+
file: ui,
455+
line: 1,
456+
column: 10,
457+
context: 'View / @sim/emcn#Button / className/then',
458+
value: 'text-white',
459+
reason: 'owned chrome',
460+
observedFrom: [ui],
461+
}
462+
const uses = [
463+
{
464+
id: 'control',
465+
file: ui,
466+
line: 1,
467+
column: 1,
468+
endLine: 1,
469+
endColumn: 40,
470+
owner: 'View',
471+
tag: 'Button',
472+
slots: [{ name: 'className', line: 1, column: 5, endLine: 1, endColumn: 30 }],
473+
},
474+
]
475+
expect(associateFindings(uses, [finding], () => ['@sim/emcn#Button']).get('control')).toEqual({
476+
direct: ['branch'],
477+
potential: [],
478+
})
479+
})
480+
test.each([
481+
'CSS assignment source is nonregular or exceeds the parsing limit',
482+
'Artwork parser failure; only blob change is known',
483+
'Artwork source exceeds the 2 MiB parsing limit',
484+
'Artwork symlink/submodule is not followed',
485+
])('inspection failures are not unresolved styling: %s', (reason) => {
486+
expect(inspectionFailure(reason)).toBe(true)
487+
})

‎scripts/check-design-conformance-io.test.ts‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,30 @@ test('unwritable CLI output remains an operational exit, not an uncaught retry',
227227
expect(result.status).toBe(2)
228228
expect(result.stderr.toString()).toContain('failed')
229229
})
230+
231+
test.each(['missing', 'invalid'])(
232+
'invalid review ledger still writes a failed CLI artifact: %s',
233+
(kind) => {
234+
const ledger = path.join(repo, `ledger-${kind}.json`)
235+
if (kind === 'invalid') writeFileSync(ledger, '{')
236+
const output = path.join(repo, `ledger-report-${kind}.json`)
237+
const result = spawnSync('bun', [
238+
'--no-env-file',
239+
cli,
240+
'--policy',
241+
'appearance',
242+
'--repo',
243+
repo,
244+
'--base',
245+
base,
246+
'--head',
247+
head,
248+
'--reviews',
249+
ledger,
250+
'--output',
251+
output,
252+
])
253+
expect(result.status).toBe(2)
254+
expect(JSON.parse(readFileSync(output, 'utf8')).status).toBe('failed')
255+
}
256+
)

‎scripts/check-design-conformance-recipes.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,9 @@ test('block-local recipe aliases cannot shadow the following outer return', () =
171171
expect(values(first)).not.toEqual(values(first.replace('tier=7', 'tier=8')))
172172
expect(values(first)).toEqual(values(first.replace('tier=12', 'tier=13')))
173173
})
174+
175+
test('returned closures retain captured lexical recipe values', () => {
176+
const code = 'export function choose(){const local=7;return ()=>local}'
177+
expect(values(code)).not.toEqual(values(code.replace('local=7', 'local=8')))
178+
expect(extract(code).unchecked).toEqual([])
179+
})

‎scripts/check-design-conformance-tokens.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,32 @@ describe('only introduced violations', () => {
264264
expect(existsSync(marker)).toBe(false)
265265
})
266266
})
267+
268+
test('checked-to-unsupported renames preserve coverage diagnostics', () => {
269+
const report = diff(
270+
element('text-[#123456]'),
271+
element('text-[#123456]'),
272+
'apps/sim/components/view.md',
273+
ui
274+
)
275+
expect(report.coverage.unsupportedFiles).toBe(1)
276+
expect(
277+
report.unchecked.some(
278+
(n) =>
279+
n.file === 'apps/sim/components/view.md' &&
280+
n.side === 'after' &&
281+
n.reason.includes('Unsupported file format')
282+
)
283+
).toBe(true)
284+
})
285+
test.each(['currentcolor', 'CURRENTCOLOR', 'currentColor'])(
286+
'CSS current color is case insensitive: %s',
287+
(value) => {
288+
expect(diff('', `.label{color:${value}}`, 'apps/sim/components/a.css').findings).toEqual([])
289+
}
290+
)
291+
test('literal ring offset colours are inspected', () => {
292+
expect(
293+
diff('', element('ring-offset-[#123456]')).findings.some((f) => f.category === 'colours')
294+
).toBe(true)
295+
})

‎scripts/check-design-conformance-typography.test.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,12 @@ test('font-size tokens cannot approve a font-weight declaration', () => {
174174
review({ 'apps/sim/components/a.css': '.a {font-weight:var(--text-sm)}' }).classifications
175175
).toEqual([])
176176
})
177+
178+
test('uninspected CSS cannot establish global weight token ownership', () => {
179+
const result = review({
180+
'apps/sim/components/gap.css': '.gap{',
181+
'apps/sim/components/a.css': '.a{font-weight:var(--font-weight-semibold)}',
182+
})
183+
expect(result.classifications).toEqual([])
184+
expect(result.unchecked.some((n) => n.file === 'apps/sim/components/gap.css')).toBe(true)
185+
})

‎scripts/design-conformance/central-recipes.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export function extractCentralRecipes(
164164
t.isArrowFunctionExpression(node)
165165
) {
166166
if (node.async || node.generator) return fail()
167-
const inner = new Map(bindings)
167+
const inner = new Map(scope)
168168
const defaults: Value[] = []
169169
node.params.forEach((p, i) => {
170170
const id = t.isAssignmentPattern(p) ? p.left : p

‎scripts/design-conformance/cli.ts‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,19 @@ try {
5656
} catch (error) {
5757
report = linter.report(null, error instanceof Error ? error.message : 'Operational failure')
5858
}
59-
if (args.reviews && report.status === 'completed')
60-
report.reviewDecisions = matchReviews(
61-
readReviewLedger(args.reviews as string, args.repo as string),
62-
report.findings,
63-
[]
64-
)
59+
if (args.reviews && report.status === 'completed') {
60+
try {
61+
report.reviewDecisions = matchReviews(
62+
readReviewLedger(args.reviews as string, args.repo as string),
63+
report.findings,
64+
[]
65+
)
66+
} catch (error) {
67+
report.status = 'failed'
68+
report.flagged = null
69+
report.error = error instanceof Error ? error.message : 'Review ledger inspection failed'
70+
}
71+
}
6572
if (output) writeJson(output, report)
6673
else process.stdout.write(`${JSON.stringify(report, null, 2)}\n`)
6774
process.exitCode = report.status === 'failed' ? 2 : report.flagged ? 1 : 0

0 commit comments

Comments
 (0)