test(m3e): живой инвариант покрытия реестра карточками + конфликт-целостность

Новый tools/m3e-card-coverage-invariants.test.mjs на реальном реестре:
(1) у каждого узла nodes.yaml есть карточка skill===slug (missingContracts пуст);
(2) нет пустых карточек (G-B); (3) конфликт-рёбра резолвятся и симметричны (G-H);
(4) реестр контрактов без формальных ошибок/дублей/дрейфа. GREEN — покрытие 86/86.
Конфликт-проверки пока тривиальны (0 рёбер), наполнятся в Task 4.

coverage: skill:test-driven-development
This commit is contained in:
Дмитрий
2026-06-08 18:56:10 +03:00
parent 779230eb58
commit e4f1344276
@@ -0,0 +1,34 @@
import { describe, it, expect } from 'vitest';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { loadRegistry as loadNodes, clearCache } from './registry-load.mjs';
import { loadRegistry as loadContracts } from './skill-contract-registry.mjs';
import { buildNodeGraph } from './node-graph.mjs';
import { missingContracts, emptyCards, unresolvedConflicts, asymmetricConflicts } from './card-coverage.mjs';
const here = dirname(fileURLToPath(import.meta.url));
const registryPath = join(here, '..', 'docs', 'registry', 'nodes.yaml');
const contractsDir = join(here, '..', 'docs', 'registry', 'contracts');
describe('Машина 3-E — покрытие реестра карточками (живой инвариант H)', () => {
it('у каждого узла nodes.yaml есть карточка с skill===slug', () => {
clearCache();
const nodes = loadNodes({ registryPath, useCache: false }).nodes;
const { contracts } = loadContracts({ dir: contractsDir });
expect(missingContracts(nodes, contracts)).toEqual([]);
});
it('нет пустых карточек (needs∪produces непусто)', () => {
const { contracts } = loadContracts({ dir: contractsDir });
expect(emptyCards(contracts)).toEqual([]);
});
it('конфликт-рёбра резолвятся и симметричны', () => {
clearCache();
const g = buildNodeGraph(loadNodes({ registryPath, useCache: false }));
expect(unresolvedConflicts(g)).toEqual([]);
expect(asymmetricConflicts(g)).toEqual([]);
});
it('реестр контрактов собирается без формальных ошибок и дублей', () => {
const { errors } = loadContracts({ dir: contractsDir });
expect(errors).toEqual([]);
});
});