62 lines
3.3 KiB
JavaScript
62 lines
3.3 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* coverage-wiring — тонкий мост между списком рекомендованных навыков и машиной охвата.
|
|||
|
|
* Заменяет реестр цепочек L механической проверкой полноты: грузит контракты с замком
|
|||
|
|
* словаря (D5), выбирает рекомендованные, считает readinessChecklist и отдаёт карточки +
|
|||
|
|
* вердикт готовности для живого гейта судьи. Без LLM (set/graph-операции).
|
|||
|
|
*
|
|||
|
|
* `ready` (спека D3/D4) = НЕТ ДЫР покрытия И НЕТ ЦИКЛА: твёрдый стоп печати — на дыре
|
|||
|
|
* (нужда, которую никто в наборе не производит и которой нет в данностях). Сироты
|
|||
|
|
* (scope-creep) и непокрытые просьбы — мягкие сигналы в `items`, печать не стопорят.
|
|||
|
|
*/
|
|||
|
|
import fsDefault from 'node:fs';
|
|||
|
|
import { loadRegistry, dispatchContract } from './skill-contract-registry.mjs';
|
|||
|
|
import { loadVocabulary } from './capability-vocabulary.mjs';
|
|||
|
|
import { readinessChecklist } from './coverage-machine.mjs';
|
|||
|
|
import { loadInitialInputs } from './registry-initial-inputs.mjs';
|
|||
|
|
|
|||
|
|
const HOLES_POINTER = '§A findHoles';
|
|||
|
|
const CYCLE_POINTER = '§A topoOrder';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Построить вход охвата для гейта по рекомендованным навыкам.
|
|||
|
|
* 1) loadRegistry с vocabTokens (D5 — замок словаря на живом пути);
|
|||
|
|
* 2) выбрать контракты рекомендованных навыков (dispatchContract, mode 'exact');
|
|||
|
|
* 3) readinessChecklist({ выбранные, requests, initialInputs:данности });
|
|||
|
|
* 4) → { cards, ready (нет дыр && нет цикла), holes, cycle, items, errors }.
|
|||
|
|
* fsImpl/dir/vocabPath инъектируются (тесты — синтетические контракты).
|
|||
|
|
*/
|
|||
|
|
export function buildCoverageInput({
|
|||
|
|
recommendedSkills = [],
|
|||
|
|
dir = 'docs/registry/contracts',
|
|||
|
|
vocabPath = 'docs/registry/capability-vocabulary.json',
|
|||
|
|
requests = [],
|
|||
|
|
fsImpl = fsDefault,
|
|||
|
|
} = {}) {
|
|||
|
|
const vocab = loadVocabulary({ path: vocabPath, fsImpl });
|
|||
|
|
const registry = loadRegistry({ dir, fsImpl, vocabTokens: vocab.tokens }); // D5: замок на живом пути
|
|||
|
|
const initialInputs = loadInitialInputs({ path: vocabPath, fsImpl });
|
|||
|
|
|
|||
|
|
const selected = [];
|
|||
|
|
for (const skill of recommendedSkills || []) {
|
|||
|
|
const d = dispatchContract(registry, skill);
|
|||
|
|
if (d.mode === 'exact' && d.contract) selected.push(d.contract);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const cards = selected.map((c) => ({
|
|||
|
|
skill: c.skill,
|
|||
|
|
needs: c.needs || [],
|
|||
|
|
produces: c.produces || [],
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const checklist = readinessChecklist({ contracts: selected, requests, initialInputs });
|
|||
|
|
const items = checklist.items || [];
|
|||
|
|
const holes = (items.find((i) => i && i.pointer === HOLES_POINTER) || {}).detail || [];
|
|||
|
|
const cycle = (items.find((i) => i && i.pointer === CYCLE_POINTER) || {}).detail || null;
|
|||
|
|
|
|||
|
|
// Спека D3/D4: стоп только на дыре/цикле (полнота+корректность), не на сироте/scope-creep.
|
|||
|
|
const ready = holes.length === 0 && cycle === null;
|
|||
|
|
|
|||
|
|
return { cards, ready, holes, cycle, items, errors: registry.errors };
|
|||
|
|
}
|