Skip to content

Commit 14d2bee

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(design): clarify diagnostics and verify scanner Studio handoff
1 parent 338b30b commit 14d2bee

3 files changed

Lines changed: 95 additions & 10 deletions

File tree

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,16 @@ test('large finding messages stay bounded in logs while JSON keeps full evidence
10811081
expect(JSON.stringify(report)).toContain('a'.repeat(3000))
10821082
})
10831083

1084+
test('long authored input cannot hide warning source or permitted action', () => {
1085+
const report = findingReport()
1086+
report.findings[0].provenance!.input = 'a'.repeat(3000)
1087+
const annotation = githubAnnotations(report)[0]
1088+
expect(annotation).toContain(`source: ${TOKEN_FILE}`)
1089+
expect(annotation).toContain('permitted: central variable')
1090+
expect(annotation).toContain('input:')
1091+
expect(JSON.stringify(report)).toContain('a'.repeat(3000))
1092+
})
1093+
10841094
test('large unchecked summaries keep complete JSON evidence and bound human output', () => {
10851095
const report = new ConformanceLinter().report(null)
10861096
report.unchecked = Array.from({ length: 101 }, (_, index) => ({
@@ -1102,6 +1112,23 @@ test('large unchecked summaries keep complete JSON evidence and bound human outp
11021112
expect(JSON.stringify(report)).toContain('component-100.tsx')
11031113
})
11041114

1115+
test('local text output names a command that exposes omitted diagnostics', () => {
1116+
const report = new ConformanceLinter().report(null)
1117+
report.unchecked = Array.from({ length: 21 }, (_, index) => ({
1118+
file: `component-${index}.tsx`,
1119+
line: 1,
1120+
side: 'after',
1121+
context: '',
1122+
reason: `Unresolved input ${index}`,
1123+
relevant: true,
1124+
}))
1125+
const output = textReport(report)
1126+
expect(output).toContain('bun run check:design')
1127+
expect(output).toContain('--output')
1128+
expect(output).toContain('--format json')
1129+
expect(output).not.toContain('component-20.tsx:1')
1130+
})
1131+
11051132
test('inspection severity is explicit and does not depend on diagnostic wording', () => {
11061133
expect(inspectionFailure({ reason: 'Arbitrary prose', inspection: 'failed' })).toBe(true)
11071134
expect(inspectionFailure({ reason: 'Parser failure in a supported but unresolved flow' })).toBe(

‎scripts/design-conformance/reporting.ts‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { Finding, Report } from '#design-conformance/model'
22

33
/** Keep source-authored newlines from becoming terminal workflow commands. */
44
const line = (value: string) => value.replaceAll('\r', '\\r').replaceAll('\n', '\\n')
5-
const bounded = (value: string) =>
6-
value.length > 1000 ? `${value.slice(0, 1000)}… [truncated; see JSON]` : value
5+
const bounded = (value: string, limit = 1000) =>
6+
value.length > limit ? `${value.slice(0, limit)}… [truncated; see JSON]` : value
77

88
export function findingCounts(report: Report) {
99
return {
@@ -13,16 +13,21 @@ export function findingCounts(report: Report) {
1313
}
1414

1515
function description(finding: Finding): string {
16-
const parts = [finding.reason, `input: ${finding.provenance?.input ?? finding.value}`]
17-
if (finding.kind === 'system-change')
16+
const parts = [bounded(finding.reason, 250)]
17+
if (finding.contract) parts.push(`contract: ${bounded(finding.contract, 160)}`)
18+
if (finding.provenance) {
1819
parts.push(
19-
`${finding.property}: ${JSON.stringify(finding.before ?? null)} → ${JSON.stringify(finding.value)}`
20+
`source: ${bounded(finding.provenance.source, 180)}`,
21+
`permitted: ${bounded(finding.provenance.permitted, 180)}`
2022
)
21-
if (finding.contract) parts.push(`contract: ${finding.contract}`)
22-
if (finding.provenance) {
23-
parts.push(`source: ${finding.provenance.source}`, `permitted: ${finding.provenance.permitted}`)
24-
if (finding.provenance.composition) parts.push(`through: ${finding.provenance.composition}`)
23+
if (finding.provenance.composition)
24+
parts.push(`through: ${bounded(finding.provenance.composition, 120)}`)
2525
}
26+
parts.push(`input: ${bounded(finding.provenance?.input ?? finding.value, 180)}`)
27+
if (finding.kind === 'system-change')
28+
parts.push(
29+
`${finding.property}: ${bounded(JSON.stringify(finding.before ?? null), 180)} → ${bounded(JSON.stringify(finding.value), 180)}`
30+
)
2631
return parts.join('; ')
2732
}
2833

@@ -68,7 +73,7 @@ export function textReport(report: Report): string {
6873
lines.push(` ${bounded(description)}`)
6974
}
7075
lines.push(
71-
`Showing ${Math.min(relevant.length, 20)} of ${report.unchecked.length} unchecked diagnostics; complete evidence is in the JSON report. No findings does not prove complete coverage.`
76+
`Showing ${Math.min(relevant.length, 20)} of ${report.unchecked.length} unchecked diagnostics. Rerun the same bun run check:design command with --format json to print the complete report, or --output /tmp/design-check.json to save it. No findings does not prove complete coverage.`
7277
)
7378
}
7479
return `${lines.join('\n')}\n`

‎scripts/design-studio/refresh.test.mjs‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,59 @@ function guardedRefresh() {
154154
return { repo, refresh }
155155
}
156156

157+
test('Studio consumes the unmodified report written by the scanner CLI', () => {
158+
const { repo } = guardedRefresh()
159+
write(
160+
repo,
161+
'apps/sim/app/product/page.tsx',
162+
"import { Example } from '@sim/emcn'\nexport default function Page() { return <div style={{ color: '#123456' }}><Example /></div> }\n"
163+
)
164+
const root = path.dirname(repo)
165+
const scan = path.join(root, 'real-scan')
166+
const scanner = path.resolve('scripts/design-scan/scan.ts')
167+
const scanned = spawnSync(
168+
process.env.DESIGN_TEST_BUN ?? 'bun',
169+
['--no-env-file', scanner, '--repo', repo, '--working-tree', '--output', scan],
170+
{ encoding: 'utf8', timeout: 120000 }
171+
)
172+
assert.ok([0, 1].includes(scanned.status), scanned.stderr)
173+
const reportFile = path.join(scan, 'scan.json')
174+
const reportBytes = readFileSync(reportFile)
175+
const report = JSON.parse(reportBytes.toString())
176+
assert.equal(report.version, 2)
177+
assert.ok(report.inventory.metadata.exports.Example)
178+
assert.ok(report.inventory.findings.some((finding) => finding.value === '#123456'))
179+
180+
const output = path.join(root, 'real-studio')
181+
const refreshed = spawnSync('node', [script], {
182+
env: {
183+
...process.env,
184+
SIM_STUDIO_REPO: repo,
185+
SIM_STUDIO_SCAN_DIR: scan,
186+
SIM_STUDIO_OUTPUT: output,
187+
},
188+
encoding: 'utf8',
189+
timeout: 120000,
190+
})
191+
assert.ok([0, 1].includes(refreshed.status), refreshed.stderr)
192+
assert.deepEqual(readFileSync(reportFile), reportBytes)
193+
const pointer = JSON.parse(readFileSync(path.join(output, 'latest.json'), 'utf8'))
194+
const manifest = JSON.parse(readFileSync(path.join(pointer.path, 'manifest.json'), 'utf8'))
195+
const example = components(manifest).find((entry) => entry.name === 'Example')
196+
assert.ok(example)
197+
assert.ok(example.variants.some((entry) => entry.id === 'component:Example:variant=filled'))
198+
assert.ok(
199+
example.usages.some(
200+
(usage) => usage.file === 'apps/sim/app/product/page.tsx' && usage.relationship === 'direct'
201+
)
202+
)
203+
assert.ok(
204+
extras(manifest).some((entry) =>
205+
report.inventory.findings.some((finding) => entry.id === `finding:${finding.id}`)
206+
)
207+
)
208+
}, 120000)
209+
157210
test('refresh tracks source changes in imported stylesheets', () => {
158211
const { repo, refresh } = guardedRefresh()
159212
const first = refresh()

0 commit comments

Comments
 (0)