chore: update vendored skills and record provenance

humanizer 2.2.0 -> 2.9.1 (blader/humanizer, MIT): adds a Voice
Calibration section and a passive-voice pattern, reworks negative
parallelisms and em dashes. Version moved to metadata.version upstream.

impeccable 3.6.0 -> 4.0.4 (pbakaus/impeccable, Apache-2.0): the repo
tags the skill and the npm CLI separately, so 3.6.0 was a real release
and npm's 3.5.0 was never the comparison. Adds native-platform reference
briefs.

Each now carries an UPSTREAM file; bin/check-vendored.sh reports drift.
This commit is contained in:
naps62
2026-08-01 14:25:26 +00:00
parent 2f7f512cec
commit 9023ed3ef9
144 changed files with 28302 additions and 5511 deletions
@@ -530,7 +530,11 @@ if (IS_BROWSER) {
function generateSelector(el) {
if (el === document.body) return 'body';
if (el === document.documentElement) return 'html';
if (el.id) return '#' + CSS.escape(el.id);
// Read via getAttribute when `el.id` is not a string — a <form> with a
// named control (e.g. <input name="id">) shadows the builtin getter and
// returns the element, producing a garbage `#[object …]` selector (#407).
const elId = typeof el.id === 'string' ? el.id : (el.getAttribute('id') || '');
if (elId) return '#' + CSS.escape(elId);
const parts = [];
let current = el;
@@ -1222,8 +1226,13 @@ if (IS_BROWSER) {
return {
type: f.type || f.id,
category: ap ? ap.category : 'quality',
severity: ap?.severity || 'warning',
severity: f.severity || ap?.severity || 'warning',
// Advisory findings (em-dash overuse, etc.) are surfaced but never
// treated as failures; carry the flag so the overlay/extension can
// render them with the mildest affordance and consumers can filter.
advisory: (ap && ap.advisory === true) || f.advisory === true,
detail: f.detail || f.snippet,
ignoreValue: f.ignoreValue || f.value || '',
name: ap ? ap.name : (f.type || f.id),
description: ap ? ap.description : '',
};
@@ -1260,20 +1269,213 @@ if (IS_BROWSER) {
return [...groupMap.entries()].map(([el, findings]) => ({ el, findings }));
}
const DESIGN_COLOR_TOLERANCE = 6;
const DESIGN_RADIUS_TOLERANCE_PX = 0.5;
const DESIGN_SKIP_TAGS = new Set(['head', 'title', 'meta', 'link', 'style', 'script', 'noscript', 'template', 'source']);
function normalizeBrowserFontName(value) {
return String(value || '')
.trim()
.replace(/^["']|["']$/g, '')
.replace(/\+/g, ' ')
.replace(/\s+/g, ' ')
.toLowerCase();
}
function browserPrimaryFont(stack) {
if (!stack || /var\(/i.test(stack)) return '';
return String(stack || '')
.split(',')
.map(normalizeBrowserFontName)
.find(font => font && !GENERIC_FONTS.has(font)) || '';
}
function browserDesignSystemConfig() {
const raw = window.__IMPECCABLE_CONFIG__?.designSystem;
if (!raw?.present) return null;
const allowedFonts = new Set((raw.allowedFonts || []).map(normalizeBrowserFontName).filter(Boolean));
const allowedColors = (raw.allowedColors || [])
.filter(color => color && Number.isFinite(color.r) && Number.isFinite(color.g) && Number.isFinite(color.b))
.map(color => ({ r: color.r, g: color.g, b: color.b }));
const allowedRadii = (raw.allowedRadii || [])
.map(Number)
.filter(px => Number.isFinite(px));
return {
present: true,
hasFonts: raw.hasFonts === true && allowedFonts.size > 0,
allowedFonts,
hasColors: raw.hasColors === true && allowedColors.length > 0,
allowedColors,
hasRadii: raw.hasRadii === true && allowedRadii.length > 0,
allowedRadii,
hasPillRadius: raw.hasPillRadius === true,
};
}
function browserColorsClose(a, b) {
if (!a || !b) return false;
return Math.max(
Math.abs(a.r - b.r),
Math.abs(a.g - b.g),
Math.abs(a.b - b.b),
) <= DESIGN_COLOR_TOLERANCE;
}
function isBrowserDesignColorAllowed(raw, designSystem) {
if (!designSystem?.hasColors) return true;
const text = String(raw || '').trim().toLowerCase();
if (!text || text === 'transparent' || text === 'currentcolor' || text === 'inherit' || text === 'initial') return true;
if (text.includes('var(')) return true;
const parsed = parseAnyColor(text);
if (!parsed) return true;
if ((parsed.a ?? 1) <= 0.05) return true;
return designSystem.allowedColors.some(color => browserColorsClose(parsed, color));
}
function isBrowserTransparentCss(value) {
const text = String(value || '').trim().toLowerCase();
if (!text || text === 'transparent') return true;
const parsed = parseAnyColor(text);
return parsed ? (parsed.a ?? 1) <= 0.05 : false;
}
function isBrowserDesignRadiusAllowed(raw, designSystem) {
if (!designSystem?.hasRadii) return true;
const text = String(raw || '').trim().toLowerCase();
if (!text || text === '0' || text === 'none' || text === 'initial' || text === 'inherit') return true;
if (text.includes('var(') || text.includes('%')) return true;
const px = resolveLengthPx(text, 16);
if (px == null || !Number.isFinite(px) || px <= DESIGN_RADIUS_TOLERANCE_PX) return true;
if (designSystem.hasPillRadius && px >= 99) return true;
return designSystem.allowedRadii.some(allowed => Math.abs(allowed - px) <= DESIGN_RADIUS_TOLERANCE_PX);
}
function browserRadiusTokens(value) {
return String(value || '')
.replace(/\s*\/\s*/g, ' ')
.split(/\s+/)
.map(token => token.trim())
.filter(Boolean);
}
function browserHasDirectText(el) {
return [...(el.childNodes || [])].some(node => node.nodeType === 3 && node.textContent.trim().length > 0);
}
function browserSampleText(el) {
const text = String(el.textContent || '').replace(/\s+/g, ' ').trim();
return text ? ` "${text.slice(0, 40)}"` : '';
}
function shouldSkipDesignElement(el) {
const tag = el.tagName?.toLowerCase?.() || '';
return DESIGN_SKIP_TAGS.has(tag) || isElementHidden(el);
}
function checkElementDesignSystemDOM(el, designSystem, seen) {
if (!designSystem?.present || shouldSkipDesignElement(el)) return [];
const findings = [];
const tag = el.tagName?.toLowerCase?.() || 'unknown';
const style = getComputedStyle(el);
if (designSystem.hasFonts && browserHasDirectText(el)) {
const font = browserPrimaryFont(style.fontFamily || '');
if (font && !designSystem.allowedFonts.has(font) && !seen.fonts.has(font)) {
seen.fonts.add(font);
findings.push({
type: 'design-system-font',
detail: `${tag}${browserSampleText(el)} uses ${font}; not declared in DESIGN.md typography`,
ignoreValue: font,
});
}
}
if (designSystem.hasColors) {
const colorChecks = [];
if (browserHasDirectText(el)) colorChecks.push(['text color', style.color]);
if (!isBrowserTransparentCss(style.backgroundColor)) colorChecks.push(['background', style.backgroundColor]);
for (const side of ['Top', 'Right', 'Bottom', 'Left']) {
if ((parseFloat(style[`border${side}Width`]) || 0) > 0) {
colorChecks.push([`border-${side.toLowerCase()}`, style[`border${side}Color`]]);
}
}
if ((parseFloat(style.outlineWidth) || 0) > 0) colorChecks.push(['outline', style.outlineColor]);
for (const [kind, raw] of colorChecks) {
const label = String(raw || '').trim().replace(/\s+/g, ' ');
if (isBrowserDesignColorAllowed(label, designSystem)) continue;
const key = `${kind}:${label}`;
if (seen.colors.has(key)) continue;
seen.colors.add(key);
findings.push({
type: 'design-system-color',
detail: `${kind} ${label} on ${tag}${browserSampleText(el)} is outside DESIGN.md colors`,
ignoreValue: label,
});
}
}
if (designSystem.hasRadii) {
for (const token of browserRadiusTokens(style.borderRadius || '')) {
if (isBrowserDesignRadiusAllowed(token, designSystem)) continue;
if (seen.radii.has(token)) continue;
seen.radii.add(token);
findings.push({
type: 'design-system-radius',
detail: `border-radius ${token} on ${tag}${browserSampleText(el)} is outside the DESIGN.md rounded scale`,
ignoreValue: token,
});
}
}
return findings;
}
function decodeBrowserGoogleFamily(value) {
const family = String(value || '').split(':')[0].replace(/\+/g, ' ');
try {
return decodeURIComponent(family);
} catch {
return family;
}
}
function checkBrowserDesignSystemSources(designSystem, seen) {
if (!designSystem?.hasFonts) return [];
const findings = [];
for (const link of document.querySelectorAll('link[href*="fonts.googleapis.com/css"]')) {
const href = link.getAttribute('href') || '';
for (const match of href.matchAll(/[?&]family=([^&]+)/g)) {
const display = decodeBrowserGoogleFamily(match[1]);
const font = normalizeBrowserFontName(display);
if (!font || designSystem.allowedFonts.has(font) || seen.fonts.has(font)) continue;
seen.fonts.add(font);
findings.push({
type: 'design-system-font',
detail: `Google Fonts: ${display} is not declared in DESIGN.md typography`,
ignoreValue: display,
});
}
}
return findings;
}
function collectBrowserFindings() {
const groupMap = new Map();
const _disabled = EXTENSION_MODE ? (window.__IMPECCABLE_CONFIG__?.disabledRules || []) : [];
const _ruleOk = (id) => !_disabled.length || !_disabled.includes(id);
// Note: provider-gated rules (--gpt / --gemini) are NOT filtered here. In a
// real browser env (detector page, live overlay, extension) running every
// check is free, so we always surface them; the gating is purely a CLI
// output concern, applied in the Node engines' detect* return paths.
const designSystem = browserDesignSystemConfig();
const designSeen = { fonts: new Set(), colors: new Set(), radii: new Set() };
// All deterministic rules run in the browser and extension path.
for (const el of document.querySelectorAll('*')) {
// Skip impeccable's own elements and any descendants (overlays, labels, banner, nav buttons)
if (el.closest('.impeccable-overlay, .impeccable-label, .impeccable-banner, .impeccable-tooltip')) continue;
// Skip browser extension elements (Claude, etc.)
const elId = el.id || '';
// Skip browser extension elements (Claude, etc.). Use getAttribute when
// `el.id` is not a string: a <form> with a named control like
// <input name="id"> shadows the builtin `id` getter and returns the
// element, whose `.startsWith` throws (issue #407).
const elId = typeof el.id === 'string' ? el.id : (el.getAttribute('id') || '');
if (elId.startsWith('claude-') || elId.startsWith('cic-')) continue;
// Skip the impeccable live-mode overlay (highlight, tooltip, bar, picker, toast).
// These are inspector chrome, not part of the user's design.
@@ -1283,10 +1485,12 @@ if (IS_BROWSER) {
const findings = [
...checkElementBordersDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementPseudoStripeDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementColorsDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementMotionDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementGlowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementAIPaletteDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementRadialSpotlightDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementIconTileDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementItalicSerifDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementQualityDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
@@ -1294,6 +1498,8 @@ if (IS_BROWSER) {
...checkElementClippedOverflowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementGptBorderShadowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementTextOverflowDOM(el).map(f => ({ type: f.id, detail: f.snippet })),
...checkElementBlinkingCursorDOM(el).map(f => ({ type: f.id, detail: f.snippet, ...(f.severity ? { severity: f.severity } : {}) })),
...checkElementDesignSystemDOM(el, designSystem, designSeen),
].filter(f => _ruleOk(f.type));
addBrowserFindings(groupMap, el, findings);
@@ -1310,13 +1516,20 @@ if (IS_BROWSER) {
const pageLevelFindings = [];
const designSourceFindings = checkBrowserDesignSystemSources(designSystem, designSeen)
.filter(f => _ruleOk(f.type));
if (designSourceFindings.length > 0) {
pageLevelFindings.push(...designSourceFindings);
addBrowserFindings(groupMap, document.body, designSourceFindings);
}
const typoFindings = checkTypography().filter(f => _ruleOk(f.type));
if (typoFindings.length > 0) {
pageLevelFindings.push(...typoFindings);
addBrowserFindings(groupMap, document.body, typoFindings);
}
const sectionKickerFindings = checkRepeatedSectionKickersDOM()
const sectionKickerFindings = checkKickerAboveHeadingDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (sectionKickerFindings.length > 0) {
@@ -1324,12 +1537,66 @@ if (IS_BROWSER) {
addBrowserFindings(groupMap, document.body, sectionKickerFindings);
}
const numberedLabelFindings = checkNumberedSectionLabelsDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (numberedLabelFindings.length > 0) {
pageLevelFindings.push(...numberedLabelFindings);
addBrowserFindings(groupMap, document.body, numberedLabelFindings);
}
const repeatedTextFindings = checkRepeatedContainerTextDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (repeatedTextFindings.length > 0) {
pageLevelFindings.push(...repeatedTextFindings);
addBrowserFindings(groupMap, document.body, repeatedTextFindings);
}
// Em-dash overuse (advisory): browser parity with the static/regex path.
// Reads rendered body text so it catches dashes written as HTML entities.
// serializeFindings stamps the advisory flag from the registry.
const emDashFindings = checkEmDashOveruseDOM()
.map(f => ({ type: f.id, detail: f.snippet }))
.filter(f => _ruleOk(f.type));
if (emDashFindings.length > 0) {
pageLevelFindings.push(...emDashFindings);
addBrowserFindings(groupMap, document.body, emDashFindings);
}
const layoutFindings = checkLayout().filter(f => _ruleOk(f.type));
for (const f of layoutFindings) {
const el = f.el || document.body;
addBrowserFindings(groupMap, el, [{ type: f.type, detail: f.detail || f.snippet }]);
}
// Heading rhythm (browser-only: needs real layout for the gap math)
const headingRhythmFindings = checkHeadingRhythmDOM().filter(f => _ruleOk(f.type));
for (const f of headingRhythmFindings) {
addBrowserFindings(groupMap, f.el || document.body, [{ type: f.type, detail: f.detail }]);
}
// Edge-flush cards in horizontal scrollers (browser-only: needs real
// layout for the scroller clip box vs card rect math)
const edgeFlushFindings = checkEdgeFlushCardsDOM().filter(f => _ruleOk(f.type));
for (const f of edgeFlushFindings) {
addBrowserFindings(groupMap, f.el || document.body, [{ type: f.type, detail: f.detail }]);
}
// Text occlusion / element overlap (browser-only: needs real layout +
// elementFromPoint to confirm what actually paints on top)
const occlusionFindings = checkTextOcclusionDOM().filter(f => _ruleOk(f.type));
for (const f of occlusionFindings) {
addBrowserFindings(groupMap, f.el || document.body, [{ type: f.type, detail: f.detail }]);
}
// First-viewport column overflow — the stretched-hero signature
// (browser-only: needs real layout for the content-extent math)
const colOverflowFindings = checkFirstViewportColumnOverflowDOM().filter(f => _ruleOk(f.type));
for (const f of colOverflowFindings) {
addBrowserFindings(groupMap, f.el || document.body, [{ type: f.type, detail: f.detail }]);
}
// Page-level quality checks (headings, etc.)
const qualityFindings = checkPageQualityDOM().filter(f => _ruleOk(f.type));
if (qualityFindings.length > 0) {
@@ -1355,7 +1622,25 @@ if (IS_BROWSER) {
}
const htmlPatternFindings = checkHtmlPatterns(docClone.outerHTML);
if (htmlPatternFindings.length > 0) {
const mapped = htmlPatternFindings.map(f => ({ type: f.id, detail: f.snippet })).filter(f => _ruleOk(f.type));
const mapped = htmlPatternFindings.map(f => {
const item = { type: f.id, detail: f.snippet };
if (f.severity) {
item.severity = f.severity;
} else if (f.id === 'pulsing-dot' && f.selector) {
// The string scan promotes header/nav dots on its own; with a live
// layout also promote dots resting in the first ~900px of the page
// (the hero region), which the source scan cannot measure.
try {
const dotEl = document.querySelector(f.selector);
if (dotEl) {
const rect = dotEl.getBoundingClientRect();
const pageTop = rect.top + (window.scrollY || 0);
if (pageTop <= 900) item.severity = 'error';
}
} catch { /* unresolvable selector: keep registry severity */ }
}
return item;
}).filter(f => _ruleOk(f.type));
pageLevelFindings.push(...mapped);
addBrowserFindings(groupMap, document.body, mapped);
}
@@ -1729,6 +2014,9 @@ if (IS_BROWSER) {
window.impeccableDetectAsync = detectAsync;
window.impeccableScan = scan;
window.impeccableScanAsync = scanAsync;
// Raw measurement for the URL engine's content-hidden-at-rest pass: it
// drives a reveal sweep from Node and thresholds the result itself.
window.impeccableMeasureHiddenText = measureHiddenTextDOM;
window.impeccableCollectVisualContrastCandidates = collectVisualContrastCandidates;
window.impeccableAnalyzeVisualContrast = analyzeVisualContrast;
window.impeccableGetLastVisualContrastAnalyses = () => lastVisualContrastAnalyses.slice();