#!/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. // // Recovery contract: writes are crash-safe (atomicWriteSync tmp+rename — a reader sees the old // file or the new one, never a partial); an interrupted run is recovered by re-running (idempotent). // // Usage: node scripts/kb-update/relabel-dialect.mjs [--dry] import { readFileSync, existsSync, realpathSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { atomicWriteSync } from './lib/atomic-write.mjs'; 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) atomicWriteSync(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') });