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.
This commit is contained in:
Kjell Tore Guttormsen 2026-07-06 10:07:52 +02:00
commit e999b74eda
54 changed files with 405 additions and 75 deletions

View file

@ -0,0 +1,194 @@
#!/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') });