From f1e8d4e70e2e224381c2d54feaa400fd686a5e33 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Mon, 31 Aug 2026 12:59:53 -0700 Subject: [PATCH] Simplify named rule extraction AI-assisted by OpenAI Codex under maintainer pbakaus's scheduled-refactor authorization. --- skill/scripts/lib/design-parser.mjs | 48 +++++++++++------------------ tests/design-parser.test.mjs | 27 ++++++++++++++++ 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/skill/scripts/lib/design-parser.mjs b/skill/scripts/lib/design-parser.mjs index cab191d69..6f240a51e 100644 --- a/skill/scripts/lib/design-parser.mjs +++ b/skill/scripts/lib/design-parser.mjs @@ -329,47 +329,37 @@ function stripBold(s) { function extractNamedRules(lines) { const rules = []; const seen = new Set(); + const addRule = (name, body, { allowDuplicate = false } = {}) => { + const key = name.toLowerCase(); + if (!allowDuplicate && seen.has(key)) return; + seen.add(key); + rules.push({ name, body }); + }; // Style A (Impeccable): "**The X Rule.** body body body" — can span lines. const joined = lines.join('\n'); - const inlineStart = /\*\*(The [^*]+?Rule)\.\*\*/g; - const inlineMatches = []; - let m; - while ((m = inlineStart.exec(joined)) !== null) { - inlineMatches.push({ name: m[1], start: m.index, end: inlineStart.lastIndex }); - } + const inlineMatches = [...joined.matchAll(/\*\*(The [^*]+?Rule)\.\*\*/g)]; for (let i = 0; i < inlineMatches.length; i++) { - const mm = inlineMatches[i]; - const bodyEnd = i + 1 < inlineMatches.length ? inlineMatches[i + 1].start : joined.length; + const match = inlineMatches[i]; + const bodyEnd = inlineMatches[i + 1]?.index ?? joined.length; const body = joined - .slice(mm.end, bodyEnd) + .slice(match.index + match[0].length, bodyEnd) .replace(/\n##[^\n]*$/s, '') .replace(/\n###[^\n]*$/s, '') .trim(); - const name = stripBold(mm.name).trim(); - seen.add(name.toLowerCase()); - rules.push({ name, body: stripBold(body) }); + // Preserve the inline format's historical behavior: repeated inline rules + // remain visible, while the later heading and bullet formats dedupe. + addRule(stripBold(match[1]).trim(), stripBold(body), { allowDuplicate: true }); } // Style B (Stitch): `### The "X" Rule` or `### The X Fallback`, body is the // bullets/paragraphs until the next heading. Accept Rule / Fallback / Principle. - for (let i = 0; i < lines.length; i++) { - const h3 = lines[i].match(/^###\s+(.+?)\s*$/); - if (!h3) continue; - const headerName = stripBold(h3[1]).replace(/["“”]/g, '').trim(); + for (const subsection of splitSubsections(lines).slice(1)) { + const headerName = stripBold(subsection.name).replace(/["“”]/g, '').trim(); if (!/^The\b.*\b(Rule|Fallback|Principle)\b/i.test(headerName)) continue; - if (seen.has(headerName.toLowerCase())) continue; - const bodyLines = []; - for (let j = i + 1; j < lines.length; j++) { - if (/^##\s|^###\s/.test(lines[j])) break; - bodyLines.push(lines[j]); - } - const body = stripBold(bodyLines.join('\n').replace(/\n+/g, ' ')).trim(); - if (body) { - seen.add(headerName.toLowerCase()); - rules.push({ name: headerName, body }); - } + const body = stripBold(subsection.lines.join('\n').replace(/\n+/g, ' ')).trim(); + if (body) addRule(headerName, body); } // Style C (Stitch bullet form): "* **The Layering Principle:** body" @@ -379,9 +369,7 @@ function extractNamedRules(lines) { if (!mm) continue; const nameRaw = mm[1].replace(/[.:]\s*$/, '').replace(/["“”]/g, '').trim(); if (!/^The\b.+\b(Rule|Fallback|Principle)$/i.test(nameRaw)) continue; - if (seen.has(nameRaw.toLowerCase())) continue; - seen.add(nameRaw.toLowerCase()); - rules.push({ name: nameRaw, body: stripBold(mm[2]).trim() }); + addRule(nameRaw, stripBold(mm[2]).trim()); } return rules; diff --git a/tests/design-parser.test.mjs b/tests/design-parser.test.mjs index db78d4f12..a2cee5962 100644 --- a/tests/design-parser.test.mjs +++ b/tests/design-parser.test.mjs @@ -228,6 +228,33 @@ describe('parseDesignMd overview branch', () => { }); }); +describe('parseDesignMd named rules', () => { + it('preserves format precedence while deduplicating later definitions', () => { + const md = `# Design System: Rules + +## Layout + +### The "Rhythm" Rule +Ignore this duplicate heading definition. + +### The Fallback Principle +Use the heading definition. + +### Named Rules +- **The Fallback Principle:** Ignore this duplicate bullet definition. +- **The Layering Principle:** Use the bullet definition. + +**The Rhythm Rule.** Keep the first definition. +`; + + assert.deepEqual(parseDesignMd(md).layout.rules, [ + { name: 'The Rhythm Rule', body: 'Keep the first definition.' }, + { name: 'The Fallback Principle', body: 'Use the heading definition.' }, + { name: 'The Layering Principle', body: 'Use the bullet definition.' }, + ]); + }); +}); + describe('parseDesignMd canonical sections', () => { it('preserves content and named rules from all eight canonical sections', () => { const md = `# Design System: Complete