Ny data/domain-taxonomy.json (tracket via gitignore-negasjon; generert registry/rapporter forblir ignorert) konsoliderer de 4 divergerende taksonomiene: (a) sitemap-prefikser, (b) discover INCLUDE/EXCLUDE, (c) category->skill, (d) getFilePriority. lib/taxonomy.mjs laster + kompilerer. Refaktor: poll-sitemaps, discover-new-urls, report-changes LESER taksonomien (ingen embeddede kopier). Kriterium grep TARGET_PREFIXES=[ == 0 oppfylt. Konsolideringsfunn: - category-skill-map.json stale i 4 entries (copilot-extensibility, monitoring-observability, performance-scalability, prompt-engineering -> sa engineering; disk sier advisor/governance/security/advisor). DISK er kanon. Map-en er ukonsumert av kode (kun README) -> flagget, ikke rort. - discover fikk poll-paritet (12 -> 18 sitemap-prefikser). - skill utledes na fra kanonisk category_skill -> ingen divergens (invariant-test). Adferdsbevarende: 0 mismatch old vs ny prioritetslogikk pa 51 endrede filer; discover sine hardkodede skill-verdier matchet allerede disk. Tester: validate 239, kb-update 49 (+7 taksonomi), kb-eval 13 - alle gronne. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REiKFhP4w6xGXXqWKpPCJJ
85 lines
2.8 KiB
JavaScript
85 lines
2.8 KiB
JavaScript
// taxonomy.mjs — Loader + accessors for data/domain-taxonomy.json (lag 0).
|
|
// Zero dependencies. The taxonomy is the single source of truth for in-domain
|
|
// classification: which sitemaps to poll, which URLs are relevant, which
|
|
// skill/category owns content, and update priority. poll-sitemaps,
|
|
// discover-new-urls and report-changes READ this instead of embedding copies.
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const DEFAULT_DATA_DIR = join(__dirname, '..', 'data');
|
|
|
|
/**
|
|
* Load the domain taxonomy from disk.
|
|
* @param {string} [dataDir] — defaults to ../data/ relative to lib/
|
|
* @returns {object} parsed taxonomy
|
|
*/
|
|
export function loadTaxonomy(dataDir = DEFAULT_DATA_DIR) {
|
|
const path = join(dataDir, 'domain-taxonomy.json');
|
|
return JSON.parse(readFileSync(path, 'utf8'));
|
|
}
|
|
|
|
/**
|
|
* Target sitemap child-name prefixes to scan/poll.
|
|
* @param {object} tax
|
|
* @returns {string[]}
|
|
*/
|
|
export function getSitemapPrefixes(tax) {
|
|
return tax.sitemap_prefixes;
|
|
}
|
|
|
|
/**
|
|
* Canonical owning skill for a KB category (physical-disk truth).
|
|
* @param {object} tax
|
|
* @param {string} category
|
|
* @returns {string|null} skill directory name, or null if unknown
|
|
*/
|
|
export function getCategorySkill(tax, category) {
|
|
return tax.category_skill[category] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Build a URL classifier mirroring discover-new-urls semantics:
|
|
* exclude wins; first include match → { skill, category } with skill DERIVED
|
|
* from the canonical category_skill map (no embedded skill assignments).
|
|
* @param {object} tax
|
|
* @returns {(url: string) => ({skill: string|null, category: string}|null)}
|
|
*/
|
|
export function makeClassifier(tax) {
|
|
const include = tax.relevance.include.map((r) => ({
|
|
re: new RegExp(r.pattern),
|
|
category: r.category,
|
|
}));
|
|
const exclude = tax.relevance.exclude.map((p) => new RegExp(p));
|
|
|
|
return function classifyUrl(url) {
|
|
if (exclude.some((re) => re.test(url))) return null;
|
|
for (const rule of include) {
|
|
if (rule.re.test(url)) {
|
|
return { skill: tax.category_skill[rule.category] ?? null, category: rule.category };
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Build a file-priority function mirroring report-changes getFilePriority:
|
|
* ordered regex rules over the lowercased path; first match wins; else default.
|
|
* @param {object} tax
|
|
* @returns {(filePath: string) => string}
|
|
*/
|
|
export function makePriorityFn(tax) {
|
|
const rules = tax.priority.rules.map((r) => ({ re: new RegExp(r.match), priority: r.priority }));
|
|
const fallback = tax.priority.default;
|
|
|
|
return function getFilePriority(filePath) {
|
|
const lower = filePath.toLowerCase();
|
|
for (const r of rules) {
|
|
if (r.re.test(lower)) return r.priority;
|
|
}
|
|
return fallback;
|
|
};
|
|
}
|