Skip to content

Commit 19b5fa4

Browse files
authored
Merge pull request #456 from pbakaus/codex/issue-436-design-coverage
Fix DESIGN.md frontmatter coverage
2 parents 42bc53e + f274ca2 commit 19b5fa4

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

skill/scripts/lib/staleness-deep.mjs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ export function checkDesignDrift({ designPath, projectRoot, threshold = 25 }) {
117117
* a section can be absent because it never applied, so this is reported as a
118118
* documentation gap for a human to judge, never as an error.
119119
*/
120+
function hasCoverageValue(value) {
121+
if (Array.isArray(value)) return value.some(hasCoverageValue);
122+
if (value && typeof value === 'object') {
123+
return Object.values(value).some(hasCoverageValue);
124+
}
125+
if (typeof value === 'string') {
126+
const trimmed = value.trim();
127+
return trimmed.length > 0 && !/^(?:\[\s*\]|\{\s*\})$/.test(trimmed);
128+
}
129+
return false;
130+
}
131+
120132
export function checkDesignCoverage({ design, designPath, parseDesignMd }) {
121133
if (!design || typeof parseDesignMd !== 'function') return [];
122134
let model;
@@ -126,7 +138,7 @@ export function checkDesignCoverage({ design, designPath, parseDesignMd }) {
126138
return [];
127139
}
128140
const missing = ['colors', 'typography', 'components']
129-
.filter((section) => !model[section]);
141+
.filter((section) => !model[section] && !hasCoverageValue(model.frontmatter?.[section]));
130142
if (!missing.length) return [];
131143
return [finding({
132144
id: 'design-md-coverage',

tests/doctor.test.mjs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,111 @@ describe('checkDesignCoverage', () => {
172172
assert.deepEqual(checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd }), []);
173173
});
174174

175+
it('counts machine-readable frontmatter as section coverage', () => {
176+
const design = [
177+
'---',
178+
'name: X',
179+
'colors:',
180+
' ink: "#111111"',
181+
'typography:',
182+
' body:',
183+
' fontFamily: Inter',
184+
'components:',
185+
' button:',
186+
' backgroundColor: "{colors.ink}"',
187+
'---',
188+
'',
189+
'# Design System: X',
190+
'',
191+
'See the canonical source for prose guidance.',
192+
'',
193+
].join('\n');
194+
assert.deepEqual(checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd }), []);
195+
});
196+
197+
it('does not count empty frontmatter mappings as section coverage', () => {
198+
const design = [
199+
'---',
200+
'name: X',
201+
'colors:',
202+
'typography:',
203+
' body:',
204+
' fontFamily: Inter',
205+
'components:',
206+
' button:',
207+
' backgroundColor: "#111111"',
208+
'---',
209+
'',
210+
'# Design System: X',
211+
'',
212+
].join('\n');
213+
const findings = checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd });
214+
assert.deepEqual(ids(findings), ['design-md-coverage']);
215+
assert.match(findings[0].summary, /no colors section/);
216+
assert.doesNotMatch(findings[0].summary, /typography|components/);
217+
});
218+
219+
it('does not count boolean or numeric frontmatter scalars as section coverage', () => {
220+
for (const colors of ['false', '0']) {
221+
const design = [
222+
'---',
223+
'name: X',
224+
`colors: ${colors}`,
225+
'typography:',
226+
' body:',
227+
' fontFamily: Inter',
228+
'components:',
229+
' button:',
230+
' backgroundColor: "#111111"',
231+
'---',
232+
'',
233+
'# Design System: X',
234+
'',
235+
].join('\n');
236+
const findings = checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd });
237+
assert.deepEqual(ids(findings), ['design-md-coverage']);
238+
assert.match(findings[0].summary, /no colors section/);
239+
}
240+
});
241+
242+
it('does not count empty frontmatter collection literals as section coverage', () => {
243+
for (const colors of ['[]', '{}']) {
244+
const design = [
245+
'---',
246+
'name: X',
247+
`colors: ${colors}`,
248+
'typography:',
249+
' body:',
250+
' fontFamily: Inter',
251+
'components:',
252+
' button:',
253+
' backgroundColor: "#111111"',
254+
'---',
255+
'',
256+
'# Design System: X',
257+
'',
258+
].join('\n');
259+
const findings = checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd });
260+
assert.deepEqual(ids(findings), ['design-md-coverage']);
261+
assert.match(findings[0].summary, /no colors section/);
262+
}
263+
});
264+
265+
it('counts populated frontmatter array literals as section coverage', () => {
266+
const design = [
267+
'---',
268+
'name: X',
269+
'colors: ["#111111"]',
270+
'typography: [Inter]',
271+
'components: [button]',
272+
'---',
273+
'',
274+
'# Design System: X',
275+
'',
276+
].join('\n');
277+
assert.deepEqual(checkDesignCoverage({ design, designPath: 'DESIGN.md', parseDesignMd }), []);
278+
});
279+
175280
it('reports nothing without a DESIGN.md', () => {
176281
assert.deepEqual(checkDesignCoverage({ design: null, parseDesignMd }), []);
177282
});

0 commit comments

Comments
 (0)