ms-ai-architect/scripts/kb-update/relabel-dialect.mjs
Kjell Tore Guttormsen e999b74eda feat(ms-ai-architect): decision-b Enhet 1 — dialekt-relabel 51 filer (Kategori→Category + Sist oppdatert→Last updated), 73 byte-eksakte swaps [skip-docs]
Ren value-preserving label-relabel av de to norske header-labelene til engelsk på 51 ref-filer (22 bærer begge). Ny testet ren primitiv relabelHeaderDialect() (header-blokk-scoped, kollisjons-/multiforekomst-guard) + manifest-drevet driver relabel-dialect.mjs (frosset 51-fil-manifest, hard per-fil-invariant, idempotent, isMain-guard). **Dato:** bevisst UTE (body-template-felle → Enhet 2). Premiss-korreksjon i roadmap R22: tredje Dato-dialekt (16), 0 bold-duplikater (ikke 4), 1 datoløs (ikke 5), category-none = vindus-artefakt. test-relabel-dialect 11/11; diff +73/-73 0 linjer utover label; suite 782/782 exit 0.
2026-07-06 10:07:52 +02:00

194 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
// Enhet 1 (decision-b dialect pass, path A, ⊥ R7) — pure value-preserving relabel of the two
// Norwegian header labels to their English equivalents:
// **Sist oppdatert:** → **Last updated:** (31 files)
// **Kategori:** → **Category:** (42 files) [22 files carry both → 73 relabels]
//
// This is a LABEL rename only: the value (date, category text, "(v1.0)" suffix) is byte-preserved,
// so no fact is ever fabricated — this is the safe core of the corrected decision-b residual.
//
// Scope is the header block (lines above the first `---` thematic break OR first `## ` heading),
// so a `**Kategori:**`/`**Sist oppdatert:**` inside a BODY template block is never touched. The
// **Dato:** dialect (16 files) is DELIBERATELY excluded: it also appears in body templates
// (`**Dato:** [YYYY-MM-DD]`) and needs a header-scoped ISO-date guard — deferred to Unit 2.
// Status backfill (27+4) and the ai-act dual-block dedup are Unit 3; decision-trees date is Unit 4.
//
// Safety: a frozen MANIFEST (ground-truth-verified 2026-07-06) + a hard per-file invariant asserted
// BEFORE any write (exactly `applied.length` lines changed, each equals the old line with only the
// label token swapped, everything else byte-identical). Idempotent: a file with no norsk header
// label is skipped, so a re-run is a no-op. Aborts and writes nothing on any drift or invariant breach.
//
// Usage: node scripts/kb-update/relabel-dialect.mjs [--dry]
import { readFileSync, writeFileSync, existsSync, realpathSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = join(__dirname, '..', '..');
// The two norsk→english label pairs. **Dato:** is intentionally absent (body-template trap).
export const LABEL_MAP = [
['**Sist oppdatert:**', '**Last updated:**'],
['**Kategori:**', '**Category:**'],
];
// The header block ends at the first thematic break (`---`) or the first `## ` section heading.
// Everything below is body (templates, prose) and is out of relabel scope.
function headerEnd(lines) {
for (let i = 0; i < lines.length; i++) {
if (/^---\s*$/.test(lines[i]) || /^##\s/.test(lines[i])) return i;
}
return lines.length;
}
/**
* Relabel the norsk header labels to English, in the header block only. Pure.
* Guards: a norsk label appearing >1× in the header block, or an English target label already
* present in the header block (would duplicate), abort. A norsk label only in the body is ignored.
* @param {string} content
* @param {[string,string][]} [labelMap]
* @returns {{content: string, applied: {from:string,to:string,line:number}[]}}
*/
export function relabelHeaderDialect(content, labelMap = LABEL_MAP) {
const lines = String(content ?? '').split('\n');
const end = headerEnd(lines);
const header = lines.slice(0, end);
const applied = [];
for (const [from, to] of labelMap) {
let occ = 0;
let idx = -1;
for (let i = 0; i < header.length; i++) {
const c = header[i].split(from).length - 1;
if (c > 0) { occ += c; idx = i; }
}
if (occ === 0) continue; // norsk label absent from header → nothing to do
if (occ > 1) throw new Error(`ABORT: '${from}' appears ${occ}× in header block`);
if (header.some((l) => l.includes(to))) throw new Error(`ABORT: target '${to}' already present in header (would duplicate)`);
header[idx] = header[idx].replace(from, to);
applied.push({ from, to, line: idx });
}
return { content: [...header, ...lines.slice(end)].join('\n'), applied };
}
// Frozen manifest — the 51 scope-confirmed targets (files carrying **Sist oppdatert:** and/or
// **Kategori:** in their header block), verified against ground truth 2026-07-06. The per-file
// invariant below re-proves each write; the manifest only bounds WHICH files may be touched.
export const MANIFEST = [
'skills/ms-ai-advisor/references/architecture/ai-utredning-template.md',
'skills/ms-ai-advisor/references/architecture/alternativanalyse-methodology.md',
'skills/ms-ai-advisor/references/architecture/capacity-feasibility-benchmarks.md',
'skills/ms-ai-advisor/references/architecture/diagram-prompt-templates.md',
'skills/ms-ai-advisor/references/architecture/regional-availability-verification.md',
'skills/ms-ai-advisor/references/architecture/source-traceability-assumption-register.md',
'skills/ms-ai-engineering/references/mlops-genaiops/cost-optimization-mlops-pipelines.md',
'skills/ms-ai-engineering/references/mlops-genaiops/feedback-loops-continuous-improvement.md',
'skills/ms-ai-engineering/references/mlops-genaiops/genaiops-llm-specific-practices.md',
'skills/ms-ai-engineering/references/mlops-genaiops/governance-audit-ml-operations.md',
'skills/ms-ai-engineering/references/mlops-genaiops/inferencing-optimization-caching.md',
'skills/ms-ai-engineering/references/mlops-genaiops/infrastructure-as-code-mlops.md',
'skills/ms-ai-engineering/references/mlops-genaiops/llm-evaluation-production.md',
'skills/ms-ai-engineering/references/mlops-genaiops/mlops-security-access-control.md',
'skills/ms-ai-engineering/references/mlops-genaiops/mlops-teams-collaboration-tools.md',
'skills/ms-ai-engineering/references/mlops-genaiops/monitoring-observability-ml-systems.md',
'skills/ms-ai-engineering/references/mlops-genaiops/prompt-flow-production-deployment.md',
'skills/ms-ai-engineering/references/mlops-genaiops/responsible-ai-mlops-integration.md',
'skills/ms-ai-governance/references/monitoring-observability/anomaly-detection-ai-systems.md',
'skills/ms-ai-governance/references/monitoring-observability/application-insights-llm-monitoring.md',
'skills/ms-ai-governance/references/monitoring-observability/azure-monitor-setup-ai-workloads.md',
'skills/ms-ai-governance/references/monitoring-observability/custom-dashboards-ai-operations.md',
'skills/ms-ai-governance/references/monitoring-observability/distributed-tracing-ai-pipelines.md',
'skills/ms-ai-governance/references/monitoring-observability/log-analytics-kql-ai-queries.md',
'skills/ms-ai-governance/references/monitoring-observability/token-usage-tracking-attribution.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/gevinstrealisering-dfo-methodology.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/norwegian-nlp-benchmarks.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-ai-threat-library.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-dpia-security-integration.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-maestro-multiagent.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-methodology-ns5814-iso31000.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-report-templates.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-scoring-rubrics-7x5.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/ros-sector-checklists.md',
'skills/ms-ai-governance/references/norwegian-public-sector-governance/samfunnsokonomisk-analyse-nnv.md',
'skills/ms-ai-governance/references/responsible-ai/ai-act-annex-iii-checklist.md',
'skills/ms-ai-governance/references/responsible-ai/ai-center-of-excellence-setup.md',
'skills/ms-ai-governance/references/responsible-ai/ai-governance-structure-framework.md',
'skills/ms-ai-governance/references/responsible-ai/red-teaming-ai-models.md',
'skills/ms-ai-security/references/ai-security-engineering/adversarial-input-robustness-testing.md',
'skills/ms-ai-security/references/ai-security-engineering/ai-prompt-shield-network.md',
'skills/ms-ai-security/references/ai-security-engineering/ai-red-team-operations-practical.md',
'skills/ms-ai-security/references/ai-security-engineering/data-leakage-prevention-ai.md',
'skills/ms-ai-security/references/ai-security-engineering/entra-agent-id-zero-trust.md',
'skills/ms-ai-security/references/ai-security-engineering/model-fingerprinting-watermarking.md',
'skills/ms-ai-security/references/ai-security-engineering/secure-model-deployment-hardening.md',
'skills/ms-ai-security/references/ai-security-engineering/security-copilot-integration.md',
'skills/ms-ai-security/references/ai-security-engineering/security-scoring-rubrics-6x5.md',
'skills/ms-ai-security/references/ai-security-engineering/supply-chain-security-ai-models.md',
'skills/ms-ai-security/references/ai-security-engineering/zero-trust-ai-services.md',
'skills/ms-ai-security/references/cost-optimization/deterministic-cost-calculation-model.md',
];
function run({ dry }) {
const planned = [];
const missing = [];
let dateRelabels = 0;
let catRelabels = 0;
for (const rel of MANIFEST) {
const abs = join(PLUGIN_ROOT, rel);
if (!existsSync(abs)) { missing.push(rel); continue; }
const old = readFileSync(abs, 'utf8');
const { content: out, applied } = relabelHeaderDialect(old);
if (applied.length === 0) continue; // idempotent — already relabeled (re-run)
// Hard invariant: exactly `applied.length` lines changed, each is the old line with ONLY
// the label token swapped; line count unchanged; everything else byte-identical.
const o = old.split('\n');
const n = out.split('\n');
if (n.length !== o.length) throw new Error(`ABORT ${rel}: line count changed (${o.length}${n.length})`);
let diffs = 0;
for (let i = 0; i < o.length; i++) {
if (o[i] === n[i]) continue;
diffs++;
const hit = applied.find((a) => a.line === i);
if (!hit) throw new Error(`ABORT ${rel}: line ${i} changed but not in applied set`);
if (n[i].replace(hit.to, hit.from) !== o[i]) throw new Error(`ABORT ${rel}: line ${i} changed beyond the label token`);
}
if (diffs !== applied.length) throw new Error(`ABORT ${rel}: ${diffs} lines changed ≠ ${applied.length} applied`);
for (const a of applied) {
if (a.from === '**Sist oppdatert:**') dateRelabels++;
else if (a.from === '**Kategori:**') catRelabels++;
}
planned.push({ rel, applied, out });
}
if (missing.length) {
console.error(`ABORT — ${missing.length} manifest target(s) not found (corpus drift):`);
missing.forEach((m) => console.error(' ' + m));
process.exit(1);
}
console.log(`Manifest: ${MANIFEST.length} | files to relabel: ${planned.length} | already-english (skipped): ${MANIFEST.length - planned.length}`);
console.log(` ${dateRelabels} **Sist oppdatert:** → **Last updated:**`);
console.log(` ${catRelabels} **Kategori:** → **Category:**`);
console.log(` ${dateRelabels + catRelabels} total relabels`);
if (dry) {
console.log('\n(dry run — no writes)');
for (const p of planned) {
console.log(` ~ ${p.rel} [${p.applied.map((a) => a.from.replace(/\*/g, '')).join(', ')}]`);
}
return;
}
for (const p of planned) writeFileSync(join(PLUGIN_ROOT, p.rel), p.out);
console.log(`\nWrote ${planned.length} files.`);
}
const isMain = (() => {
try {
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
} catch {
return false;
}
})();
if (isMain) run({ dry: process.argv.includes('--dry') });