feat(skd-validate): проверка схемы компоновки по XSD платформы (#101)

Если в проекте есть схемы /v8-xsd-fetch, skd-validate дополнительно
проверяет порядок элементов, типы и значения по XSD версии формата
выгрузки (штамп Configuration.xml или корня внешнего отчёта). Схемы
точной версии — ошибки, только более новой — предупреждения, иначе
проверка пропускается. Места, которые платформа пишет иначе, чем
описывает XSD, отсекаются по месту нарушения: на 2606 схемах компоновки
выгрузок БП, УНФ и ERP (форматы 2.17–2.21) — ни одного срабатывания в
обоих портах.

Runner: поле кейса requires (путь от корня репозитория, нет — skipped)
и плейсхолдер {repoRoot} — кейсы на настоящих схемах, которые в
репозиторий не входят.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018dc2yLQfw6wppbopM1Y4Ps
This commit is contained in:
Nick Shirokov
2026-09-29 15:24:26 +03:00
co-authored by Claude Opus 5.5
parent 9293305a12
commit 5465a7803c
25 changed files with 1594 additions and 12 deletions
+5 -3
View File
@@ -22,7 +22,7 @@ const runtime = argv.includes('--runtime') ? argv[argv.indexOf('--runtime') + 1]
const listMode = argv.includes('--list');
const os = process.platform;
const reasons = { external: [], runtimeOnly: [], osOnly: [] };
const reasons = { external: [], runtimeOnly: [], osOnly: [], requires: [] };
for (const skill of readdirSync(CASES)) {
const dir = join(CASES, skill);
@@ -38,6 +38,7 @@ for (const skill of readdirSync(CASES)) {
// Порядок совпадает с раннером: сначала ОС, потом порт, потом фикстура.
if (c.osOnly && ![].concat(c.osOnly).includes(os)) { reasons.osOnly.push(id); continue; }
if (c.runtimeOnly && c.runtimeOnly !== runtime) { reasons.runtimeOnly.push(id); continue; }
if (c.requires && ![].concat(c.requires).every(p => existsSync(join(HERE, '..', '..', p)))) { reasons.requires.push(id); continue; }
const setup = String(c.setup || skillCfg.setup || '');
if (setup.startsWith('external:') && !existsSync(setup.slice('external:'.length))) {
reasons.external.push(id);
@@ -45,7 +46,7 @@ for (const skill of readdirSync(CASES)) {
}
}
const total = reasons.external.length + reasons.runtimeOnly.length + reasons.osOnly.length;
const total = reasons.external.length + reasons.runtimeOnly.length + reasons.osOnly.length + reasons.requires.length;
if (listMode) {
for (const [key, ids] of Object.entries(reasons)) {
@@ -58,6 +59,7 @@ if (listMode) {
console.log(
`Ожидается пропущенных: ${total} [${os}, runtime: ${runtime}] ` +
`= external ${reasons.external.length} + runtimeOnly ${reasons.runtimeOnly.length} + osOnly ${reasons.osOnly.length}`,
`= external ${reasons.external.length} + runtimeOnly ${reasons.runtimeOnly.length} + osOnly ${reasons.osOnly.length}` +
` + requires ${reasons.requires.length}`,
);
console.log('Сверить с "Skipped: N" из прогона runner.mjs с теми же --runtime.');