From e4f134427697f3d69f94df8def3d43f9ebf6e2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Mon, 8 Jun 2026 18:56:10 +0300 Subject: [PATCH] =?UTF-8?q?test(m3e):=20=D0=B6=D0=B8=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B8=D0=BD=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D1=8F=20=D1=80=D0=B5=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BA=D0=B0=D0=BC=D0=B8=20+=20=D0=BA=D0=BE=D0=BD=D1=84=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D1=82-=D1=86=D0=B5=D0=BB=D0=BE=D1=81=D1=82=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый 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 --- tools/m3e-card-coverage-invariants.test.mjs | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tools/m3e-card-coverage-invariants.test.mjs diff --git a/tools/m3e-card-coverage-invariants.test.mjs b/tools/m3e-card-coverage-invariants.test.mjs new file mode 100644 index 00000000..f95725a9 --- /dev/null +++ b/tools/m3e-card-coverage-invariants.test.mjs @@ -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([]); + }); +});